11import json
22import sys
3+ import requests
4+ from datetime import datetime , timezone
35from pathlib import Path
46
57import tweepy
@@ -22,31 +24,69 @@ def handle(self, *args, **options):
2224 print (f"Publishing question { q } " )
2325 q .state = 'PUB'
2426 q .save ()
25- socials_message = "No posts to social media"
2627 if (q .socials_text ):
2728 if skip_socials :
2829 print ("Skipping posting to social media!" )
2930 else :
30- socials_message = self .post_to_x (q .socials_text )
31- mail_admins (f"Published question { q } " , socials_message )
31+ self .post_to_x ()
32+ self .post_to_bluesky (q .socials_text )
33+ self .post_to_mastodon (q .socials_text )
3234
33- def post_to_x (self , content ):
35+ def post_to_x (self ):
36+ content = "We just published a new question! Follow us on Bluesky @cppquiz.bsky.social or Mastodon @cppquiz@mastodon.online for updates. For obvious reasons, no longer post to Elon Musk's X."
3437 print (f"Posting to X: '{ content } '" )
35- try :
36- secrets_file = Path .home () / ".cppquiz-secrets.json"
37- with secrets_file .open () as f :
38- secrets = json .load (f )
39-
40- client = tweepy .Client (
41- consumer_key = secrets ["consumer_key" ], consumer_secret = secrets ["consumer_secret" ],
42- access_token = secrets ["access_token" ], access_token_secret = secrets ["access_token_secret" ],
43- )
44- response = client .create_tweet (
45- text = content
46- )
47- post_url = f"https://x.com/user/status/{ response .data ['id' ]} "
48- print (f"Posted { post_url } " )
49- return post_url
50- except Exception as e :
51- print (f"Failed to post '{ content } ' to X due to exception '{ e } '" )
52- sys .exit (1 )
38+ secrets = self .read_secrets ()
39+
40+ client = tweepy .Client (
41+ consumer_key = secrets ["consumer_key" ], consumer_secret = secrets ["consumer_secret" ],
42+ access_token = secrets ["access_token" ], access_token_secret = secrets ["access_token_secret" ],
43+ )
44+ response = client .create_tweet (
45+ text = content
46+ )
47+ post_url = f"https://x.com/user/status/{ response .data ['id' ]} "
48+
49+ def post_to_bluesky (self , content ):
50+ print (f"Posting to Bluesky: '{ content } '" )
51+ secrets = self .read_secrets ()
52+
53+ resp = requests .post (
54+ "https://bsky.social/xrpc/com.atproto.server.createSession" ,
55+ json = {"identifier" : "cppquiz.bsky.social" , "password" : secrets ["bsky_password" ]},
56+ )
57+ resp .raise_for_status ()
58+ session = resp .json ()
59+
60+ now = datetime .now (timezone .utc ).isoformat ().replace ("+00:00" , "Z" )
61+ post = {
62+ "$type" : "app.bsky.feed.post" ,
63+ "text" : content ,
64+ "createdAt" : now ,
65+ }
66+
67+ resp = requests .post (
68+ "https://bsky.social/xrpc/com.atproto.repo.createRecord" ,
69+ headers = {"Authorization" : "Bearer " + session ["accessJwt" ]},
70+ json = {
71+ "repo" : session ["did" ],
72+ "collection" : "app.bsky.feed.post" ,
73+ "record" : post ,
74+ },
75+ )
76+ resp .raise_for_status ()
77+
78+ def post_to_mastodon (self , content ):
79+ print (f"Posting to Mastodon: '{ content } '" )
80+ secrets = self .read_secrets ()
81+
82+ url = "https://mastodon.online/api/v1/statuses"
83+ auth = {"Authorization" : f"Bearer { secrets ['mastodon_token' ]} " }
84+ params = {"status" : content }
85+
86+ r = requests .post (url , data = params , headers = auth )
87+ r .raise_for_status ()
88+
89+ def read_secrets (self ):
90+ secrets_file = Path .home () / ".cppquiz-secrets.json"
91+ with secrets_file .open () as f :
92+ return json .load (f )
0 commit comments