aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorclarkzjw <[email protected]>2023-02-23 16:45:01 -0800
committerclarkzjw <[email protected]>2023-02-23 16:46:12 -0800
commitb98b8bf3ced39afcb04e705d500cd5184d8b5254 (patch)
treeb7b9491817e83452f69750655fe240702ab44e0e
parent20b8220a12fc92f95ecae267ce4eb5a80584f564 (diff)
downloadswarm2fediverse-b98b8bf3ced39afcb04e705d500cd5184d8b5254.tar.gz
- bot: support pleroma instances
- status_update() in mastodon.py currently doesn't support content_type, thus it still fallbacks to text/plain on pleroma instances when adding comments
-rw-r--r--README.md2
-rw-r--r--bot.py8
-rw-r--r--callback.py47
-rw-r--r--dbstore/peewee_store.py1
-rw-r--r--foursquare/poi.py21
5 files changed, 64 insertions, 15 deletions
diff --git a/README.md b/README.md
index 4881eb8..570e149 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ Foursquare Swarm like Telegram bot to check in at places and post to Fediverse (
14 14
15 - Supported Fediverse software 15 - Supported Fediverse software
16 - [x] Mastodon 16 - [x] Mastodon
17 - [ ] Pleroma 17 - [x] Pleroma
18 - [ ] Misskey 18 - [ ] Misskey
19 19
20- [ ] Option to set individual checkin visibility 20- [ ] Option to set individual checkin visibility
diff --git a/bot.py b/bot.py
index 7d10ddd..6479460 100644
--- a/bot.py
+++ b/bot.py
@@ -99,12 +99,16 @@ async def process_oauth_login_callback(update: FediLoginCallbackUpdate, context:
99 home_instance = user.home_instance 99 home_instance = user.home_instance
100 100
101 if len(user.access_key) == 0: 101 if len(user.access_key) == 0:
102 mastodon_client = Mastodon(client_id=client_id, client_secret=client_secret, api_base_url=home_instance) 102 mastodon_client = Mastodon(client_id=client_id, client_secret=client_secret,
103 api_base_url=home_instance, version_check_mode="none")
103 access_token = mastodon_client.log_in( 104 access_token = mastodon_client.log_in(
104 code=update.code, 105 code=update.code,
105 redirect_uri="{}{}".format(BOT_DOMAIN, FEDI_LOGIN_CALLBACK_URL), 106 redirect_uri="{}{}".format(BOT_DOMAIN, FEDI_LOGIN_CALLBACK_URL),
106 scopes=BOT_SCOPE 107 scopes=BOT_SCOPE
107 ) 108 )
109 instance_info = mastodon_client.instance_nodeinfo()
110 if instance_info["software"]["name"] == "pleroma":
111 user.home_instance_type = "pleroma"
108 user.access_key = encrypt(access_token, ENCRYPT_KEY) 112 user.access_key = encrypt(access_token, ENCRYPT_KEY)
109 user.save() 113 user.save()
110 114
@@ -207,7 +211,7 @@ async def main() -> None:
207 app=starlette_app, 211 app=starlette_app,
208 port=BOT_PORT, 212 port=BOT_PORT,
209 use_colors=False, 213 use_colors=False,
210 host="127.0.0.1", 214 host="100.93.242.2",
211 ) 215 )
212 ) 216 )
213 217
diff --git a/callback.py b/callback.py
index a1b4b2d..ecce2bf 100644
--- a/callback.py
+++ b/callback.py
@@ -7,8 +7,7 @@ from telegram.ext import CallbackContext
7 7
8from command import * 8from command import *
9from dbstore.peewee_store import get_poi_by_fsq_id 9from dbstore.peewee_store import get_poi_by_fsq_id
10from foursquare.poi import OSM_ENDPOINT 10from foursquare.poi import query_poi, query_poi_by_fsq_id, OSM_ENDPOINT
11from foursquare.poi import query_poi
12from config import BOT_SCOPE, ENCRYPT_KEY 11from config import BOT_SCOPE, ENCRYPT_KEY
13from dbstore.peewee_store import User, db, TOOT_VISIBILITY_PRIVATE, TOOT_VISIBILITY_PUBLIC, TOOT_VISIBILITY_UNLISTED 12from dbstore.peewee_store import User, db, TOOT_VISIBILITY_PRIVATE, TOOT_VISIBILITY_PUBLIC, TOOT_VISIBILITY_UNLISTED
14import uuid 13import uuid
@@ -24,7 +23,10 @@ def get_mastodon_client(user_id: int):
24 with db.connection_context(): 23 with db.connection_context():
25 user = User.get(User.telegram_user_id == user_id) 24 user = User.get(User.telegram_user_id == user_id)
26 if user.home_instance and user.access_key: 25 if user.home_instance and user.access_key:
27 return Mastodon(access_token=decrypt(user.access_key, ENCRYPT_KEY), api_base_url=user.home_instance) 26 feature_set = "pleroma" if user.home_instance_type == "pleroma" else "mainline"
27
28 return Mastodon(access_token=decrypt(user.access_key, ENCRYPT_KEY),
29 api_base_url=user.home_instance, feature_set=feature_set, version_check_mode='none', )
28 30
29 31
30def generate_toot_text(poi_name, poi_locality, poi_region, poi_lat, poi_lon): 32def generate_toot_text(poi_name, poi_locality, poi_region, poi_lat, poi_lon):
@@ -94,11 +96,12 @@ async def callback_generate_fedi_login_url(update: Update, context: ContextTypes
94 u.save() 96 u.save()
95 97
96 oauth_url = m.auth_request_url(redirect_uris="{}{}".format(BOT_DOMAIN, FEDI_LOGIN_CALLBACK_URL), 98 oauth_url = m.auth_request_url(redirect_uris="{}{}".format(BOT_DOMAIN, FEDI_LOGIN_CALLBACK_URL),
97 scopes=BOT_SCOPE, 99 scopes=BOT_SCOPE,
98 state=state) 100 state=state)
99 101
100 msg = await update.message.reply_text(PROMPT_FEDI_LOGIN, 102 msg = await update.message.reply_text(PROMPT_FEDI_LOGIN,
101 reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Login", url=oauth_url)]]), 103 reply_markup=InlineKeyboardMarkup(
104 [[InlineKeyboardButton("Login", url=oauth_url)]]),
102 parse_mode=ParseMode.MARKDOWN) 105 parse_mode=ParseMode.MARKDOWN)
103 106
104 context.user_data[PROMPT_FEDI_LOGIN] = msg.message_id 107 context.user_data[PROMPT_FEDI_LOGIN] = msg.message_id
@@ -112,9 +115,18 @@ async def callback_location_sharing(update: Update, context: ContextTypes.DEFAUL
112 context.user_data["latitude"] = update.message.venue.location.latitude 115 context.user_data["latitude"] = update.message.venue.location.latitude
113 context.user_data["longitude"] = update.message.venue.location.longitude 116 context.user_data["longitude"] = update.message.venue.location.longitude
114 117
115 poi = get_poi_by_fsq_id(context.user_data.get("fsq_id")) 118 poi = query_poi_by_fsq_id(context.user_data.get("fsq_id"))
116 content = generate_toot_text(poi["name"], poi["locality"], poi["region"], poi["latitude"], poi["longitude"]) 119 content = generate_toot_text(poi["name"], poi["locality"], poi["region"], poi["latitude"], poi["longitude"])
117 status = get_mastodon_client(update.effective_user.id).status_post(content, visibility=TOOT_VISIBILITY_PRIVATE, media_ids=[]) 120
121 # TODO
122 # mastodon.py status_update() currently does not support content_type parameter
123 with db.connection_context():
124 u = User.get(User.telegram_user_id == update.effective_user.id)
125 content_type = "text/markdown" if u.home_instance_type == "pleroma" else "text/plain"
126 status = get_mastodon_client(update.effective_user.id).status_post(content,
127 visibility=TOOT_VISIBILITY_PRIVATE,
128 content_type=content_type,
129 media_ids=[])
118 130
119 context.user_data[KEY_TOOT_STATUS_ID] = status["id"] 131 context.user_data[KEY_TOOT_STATUS_ID] = status["id"]
120 context.user_data[KEY_TOOT_STATUS_CONTENT] = content 132 context.user_data[KEY_TOOT_STATUS_CONTENT] = content
@@ -158,7 +170,8 @@ async def callback_location_keyword_search(update: Update, context: ContextTypes
158 await context.bot.delete_message(update.effective_chat.id, context.user_data.get(PROMPT_LOCATION_KEYWORD)) 170 await context.bot.delete_message(update.effective_chat.id, context.user_data.get(PROMPT_LOCATION_KEYWORD))
159 key = update.effective_message.text 171 key = update.effective_message.text
160 172
161 keyboard = await _process_location_search(key, context.user_data.get("latitude"), context.user_data.get("longitude")) 173 keyboard = await _process_location_search(key, context.user_data.get("latitude"),
174 context.user_data.get("longitude"))
162 175
163 if len(keyboard) == 0: 176 if len(keyboard) == 0:
164 msg = await update.message.reply_text(PROMPT_WAIT_LOCATION_CONFIRMATION_NO_NEARBY_POI) 177 msg = await update.message.reply_text(PROMPT_WAIT_LOCATION_CONFIRMATION_NO_NEARBY_POI)
@@ -188,9 +201,17 @@ async def _process_location_selection(context: ContextTypes.DEFAULT_TYPE) -> int
188 poi_name = poi["name"] 201 poi_name = poi["name"]
189 content = generate_toot_text(poi["name"], poi["locality"], poi["region"], poi["latitude"], poi["longitude"]) 202 content = generate_toot_text(poi["name"], poi["locality"], poi["region"], poi["latitude"], poi["longitude"])
190 else: 203 else:
191 content = generate_toot_text(poi_name, "", "", context.user_data.get("latitude"), context.user_data.get("longitude")) 204 content = generate_toot_text(poi_name, "", "", context.user_data.get("latitude"),
205 context.user_data.get("longitude"))
192 206
193 status = get_mastodon_client(context.user_data["user_id"]).status_post(content, visibility=TOOT_VISIBILITY_PRIVATE, media_ids=[]) 207 with db.connection_context():
208 u = User.get(User.telegram_user_id == context.user_data["user_id"])
209 content_type = "text/markdown" if u.home_instance_type == "pleroma" else "text/plain"
210
211 status = get_mastodon_client(context.user_data["user_id"]).status_post(content,
212 visibility=TOOT_VISIBILITY_PRIVATE,
213 content_type=content_type,
214 media_ids=[])
194 215
195 context.user_data[KEY_TOOT_STATUS_ID] = status["id"] 216 context.user_data[KEY_TOOT_STATUS_ID] = status["id"]
196 context.user_data[KEY_TOOT_STATUS_CONTENT] = content 217 context.user_data[KEY_TOOT_STATUS_CONTENT] = content
@@ -238,11 +259,13 @@ async def _process_comment(context: ContextTypes.DEFAULT_TYPE) -> int:
238 259
239async def callback_add_comment(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: 260async def callback_add_comment(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
240 context.user_data["chat_id"] = update.effective_chat.id 261 context.user_data["chat_id"] = update.effective_chat.id
262 context.user_data["user_id"] = update.effective_user.id
241 await context.bot.delete_message(update.effective_chat.id, context.user_data.get(PROMPT_ADD_COMMENT)) 263 await context.bot.delete_message(update.effective_chat.id, context.user_data.get(PROMPT_ADD_COMMENT))
242 264
243 comment = update.effective_message.text 265 comment = update.effective_message.text
244 get_mastodon_client(update.effective_user.id).status_update(id=context.user_data.get(KEY_TOOT_STATUS_ID), 266 get_mastodon_client(update.effective_user.id).status_update(id=context.user_data.get(KEY_TOOT_STATUS_ID),
245 status=f"{comment} " + context.user_data.get(KEY_TOOT_STATUS_CONTENT)) 267 status=f"{comment} " + context.user_data.get(
268 KEY_TOOT_STATUS_CONTENT))
246 context.user_data[KEY_TOOT_STATUS_CONTENT] = f"{comment} " + context.user_data.get(KEY_TOOT_STATUS_CONTENT) 269 context.user_data[KEY_TOOT_STATUS_CONTENT] = f"{comment} " + context.user_data.get(KEY_TOOT_STATUS_CONTENT)
247 270
248 return await _process_comment(context) 271 return await _process_comment(context)
diff --git a/dbstore/peewee_store.py b/dbstore/peewee_store.py
index 445816e..ed52257 100644
--- a/dbstore/peewee_store.py
+++ b/dbstore/peewee_store.py
@@ -18,6 +18,7 @@ class User(BaseModel):
18 telegram_user_id = CharField(unique=True, primary_key=True) 18 telegram_user_id = CharField(unique=True, primary_key=True)
19 access_key = CharField(max_length=256) 19 access_key = CharField(max_length=256)
20 home_instance = CharField(max_length=256) 20 home_instance = CharField(max_length=256)
21 home_instance_type = CharField(max_length=128, default="mastodon")
21 state = CharField(max_length=128) 22 state = CharField(max_length=128)
22 client_id = CharField(max_length=128) 23 client_id = CharField(max_length=128)
23 client_secret = CharField(max_length=128) 24 client_secret = CharField(max_length=128)
diff --git a/foursquare/poi.py b/foursquare/poi.py
index 39895b7..5e31dc8 100644
--- a/foursquare/poi.py
+++ b/foursquare/poi.py
@@ -4,6 +4,7 @@ from config import FSQ_API_KEY
4from dbstore.peewee_store import create_or_update_poi 4from dbstore.peewee_store import create_or_update_poi
5 5
6POI_API_ENDPOINT = "https://api.foursquare.com/v3/places/nearby?ll={}%2C{}&limit=10" 6POI_API_ENDPOINT = "https://api.foursquare.com/v3/places/nearby?ll={}%2C{}&limit=10"
7POI_DETAIL_ENDPOINT = "https://api.foursquare.com/v3/places/{}"
7POI_SEARCH_API_ENDPOINT = "https://api.foursquare.com/v3/places/search?query={}&ll={}%2C{}&radius=2000&limit=10" 8POI_SEARCH_API_ENDPOINT = "https://api.foursquare.com/v3/places/search?query={}&ll={}%2C{}&radius=2000&limit=10"
8POI_PHOTO_ENDPOINT = "https://api.foursquare.com/v3/places/{}/photos?sort=POPULAR&limit=10" 9POI_PHOTO_ENDPOINT = "https://api.foursquare.com/v3/places/{}/photos?sort=POPULAR&limit=10"
9OSM_ENDPOINT = "https://www.openstreetmap.org/?mlat={}&mlon={}&zoom=15&layers=M" 10OSM_ENDPOINT = "https://www.openstreetmap.org/?mlat={}&mlon={}&zoom=15&layers=M"
@@ -35,6 +36,26 @@ def query_poi(search, latitude, longitude):
35 return locations 36 return locations
36 37
37 38
39def query_poi_by_fsq_id(fsq_id):
40 url = POI_DETAIL_ENDPOINT.format(fsq_id)
41 response = requests.get(url, headers=headers)
42
43 poi = json.loads(response.text)
44
45 loc = {
46 "fsq_id": fsq_id,
47 "name": poi["name"],
48 "locality": poi["location"]["locality"] if "locality" in poi["location"] else "",
49 "region": poi["location"]["region"] if "region" in poi["location"] else "",
50 "latitude": poi["geocodes"]["main"]["latitude"],
51 "longitude": poi["geocodes"]["main"]["longitude"],
52 "osm_url": OSM_ENDPOINT.format(poi["geocodes"]["main"]["latitude"], poi["geocodes"]["main"]["longitude"])
53 }
54 create_or_update_poi(loc)
55
56 return loc
57
58
38def get_poi_top_photo(fsq_id): 59def get_poi_top_photo(fsq_id):
39 url = POI_PHOTO_ENDPOINT.format(fsq_id) 60 url = POI_PHOTO_ENDPOINT.format(fsq_id)
40 response = requests.get(url, headers=headers) 61 response = requests.get(url, headers=headers)
Powered by cgit v1.2.3 (git 2.41.0)