From b98b8bf3ced39afcb04e705d500cd5184d8b5254 Mon Sep 17 00:00:00 2001 From: clarkzjw Date: Thu, 23 Feb 2023 16:45:01 -0800 Subject: - 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 --- README.md | 2 +- bot.py | 8 ++++++-- callback.py | 47 +++++++++++++++++++++++++++++++++++------------ dbstore/peewee_store.py | 1 + foursquare/poi.py | 21 +++++++++++++++++++++ 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 ( - Supported Fediverse software - [x] Mastodon - - [ ] Pleroma + - [x] Pleroma - [ ] Misskey - [ ] 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: home_instance = user.home_instance if len(user.access_key) == 0: - mastodon_client = Mastodon(client_id=client_id, client_secret=client_secret, api_base_url=home_instance) + mastodon_client = Mastodon(client_id=client_id, client_secret=client_secret, + api_base_url=home_instance, version_check_mode="none") access_token = mastodon_client.log_in( code=update.code, redirect_uri="{}{}".format(BOT_DOMAIN, FEDI_LOGIN_CALLBACK_URL), scopes=BOT_SCOPE ) + instance_info = mastodon_client.instance_nodeinfo() + if instance_info["software"]["name"] == "pleroma": + user.home_instance_type = "pleroma" user.access_key = encrypt(access_token, ENCRYPT_KEY) user.save() @@ -207,7 +211,7 @@ async def main() -> None: app=starlette_app, port=BOT_PORT, use_colors=False, - host="127.0.0.1", + host="100.93.242.2", ) ) 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 from command import * from dbstore.peewee_store import get_poi_by_fsq_id -from foursquare.poi import OSM_ENDPOINT -from foursquare.poi import query_poi +from foursquare.poi import query_poi, query_poi_by_fsq_id, OSM_ENDPOINT from config import BOT_SCOPE, ENCRYPT_KEY from dbstore.peewee_store import User, db, TOOT_VISIBILITY_PRIVATE, TOOT_VISIBILITY_PUBLIC, TOOT_VISIBILITY_UNLISTED import uuid @@ -24,7 +23,10 @@ def get_mastodon_client(user_id: int): with db.connection_context(): user = User.get(User.telegram_user_id == user_id) if user.home_instance and user.access_key: - return Mastodon(access_token=decrypt(user.access_key, ENCRYPT_KEY), api_base_url=user.home_instance) + feature_set = "pleroma" if user.home_instance_type == "pleroma" else "mainline" + + return Mastodon(access_token=decrypt(user.access_key, ENCRYPT_KEY), + api_base_url=user.home_instance, feature_set=feature_set, version_check_mode='none', ) def 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 u.save() oauth_url = m.auth_request_url(redirect_uris="{}{}".format(BOT_DOMAIN, FEDI_LOGIN_CALLBACK_URL), - scopes=BOT_SCOPE, - state=state) + scopes=BOT_SCOPE, + state=state) msg = await update.message.reply_text(PROMPT_FEDI_LOGIN, - reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Login", url=oauth_url)]]), + reply_markup=InlineKeyboardMarkup( + [[InlineKeyboardButton("Login", url=oauth_url)]]), parse_mode=ParseMode.MARKDOWN) context.user_data[PROMPT_FEDI_LOGIN] = msg.message_id @@ -112,9 +115,18 @@ async def callback_location_sharing(update: Update, context: ContextTypes.DEFAUL context.user_data["latitude"] = update.message.venue.location.latitude context.user_data["longitude"] = update.message.venue.location.longitude - poi = get_poi_by_fsq_id(context.user_data.get("fsq_id")) + poi = query_poi_by_fsq_id(context.user_data.get("fsq_id")) content = generate_toot_text(poi["name"], poi["locality"], poi["region"], poi["latitude"], poi["longitude"]) - status = get_mastodon_client(update.effective_user.id).status_post(content, visibility=TOOT_VISIBILITY_PRIVATE, media_ids=[]) + + # TODO + # mastodon.py status_update() currently does not support content_type parameter + with db.connection_context(): + u = User.get(User.telegram_user_id == update.effective_user.id) + content_type = "text/markdown" if u.home_instance_type == "pleroma" else "text/plain" + status = get_mastodon_client(update.effective_user.id).status_post(content, + visibility=TOOT_VISIBILITY_PRIVATE, + content_type=content_type, + media_ids=[]) context.user_data[KEY_TOOT_STATUS_ID] = status["id"] context.user_data[KEY_TOOT_STATUS_CONTENT] = content @@ -158,7 +170,8 @@ async def callback_location_keyword_search(update: Update, context: ContextTypes await context.bot.delete_message(update.effective_chat.id, context.user_data.get(PROMPT_LOCATION_KEYWORD)) key = update.effective_message.text - keyboard = await _process_location_search(key, context.user_data.get("latitude"), context.user_data.get("longitude")) + keyboard = await _process_location_search(key, context.user_data.get("latitude"), + context.user_data.get("longitude")) if len(keyboard) == 0: 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 poi_name = poi["name"] content = generate_toot_text(poi["name"], poi["locality"], poi["region"], poi["latitude"], poi["longitude"]) else: - content = generate_toot_text(poi_name, "", "", context.user_data.get("latitude"), context.user_data.get("longitude")) + content = generate_toot_text(poi_name, "", "", context.user_data.get("latitude"), + context.user_data.get("longitude")) - status = get_mastodon_client(context.user_data["user_id"]).status_post(content, visibility=TOOT_VISIBILITY_PRIVATE, media_ids=[]) + with db.connection_context(): + u = User.get(User.telegram_user_id == context.user_data["user_id"]) + content_type = "text/markdown" if u.home_instance_type == "pleroma" else "text/plain" + + status = get_mastodon_client(context.user_data["user_id"]).status_post(content, + visibility=TOOT_VISIBILITY_PRIVATE, + content_type=content_type, + media_ids=[]) context.user_data[KEY_TOOT_STATUS_ID] = status["id"] context.user_data[KEY_TOOT_STATUS_CONTENT] = content @@ -238,11 +259,13 @@ async def _process_comment(context: ContextTypes.DEFAULT_TYPE) -> int: async def callback_add_comment(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: context.user_data["chat_id"] = update.effective_chat.id + context.user_data["user_id"] = update.effective_user.id await context.bot.delete_message(update.effective_chat.id, context.user_data.get(PROMPT_ADD_COMMENT)) comment = update.effective_message.text get_mastodon_client(update.effective_user.id).status_update(id=context.user_data.get(KEY_TOOT_STATUS_ID), - status=f"{comment} " + context.user_data.get(KEY_TOOT_STATUS_CONTENT)) + status=f"{comment} " + context.user_data.get( + KEY_TOOT_STATUS_CONTENT)) context.user_data[KEY_TOOT_STATUS_CONTENT] = f"{comment} " + context.user_data.get(KEY_TOOT_STATUS_CONTENT) 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): telegram_user_id = CharField(unique=True, primary_key=True) access_key = CharField(max_length=256) home_instance = CharField(max_length=256) + home_instance_type = CharField(max_length=128, default="mastodon") state = CharField(max_length=128) client_id = CharField(max_length=128) 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 from dbstore.peewee_store import create_or_update_poi POI_API_ENDPOINT = "https://api.foursquare.com/v3/places/nearby?ll={}%2C{}&limit=10" +POI_DETAIL_ENDPOINT = "https://api.foursquare.com/v3/places/{}" POI_SEARCH_API_ENDPOINT = "https://api.foursquare.com/v3/places/search?query={}&ll={}%2C{}&radius=2000&limit=10" POI_PHOTO_ENDPOINT = "https://api.foursquare.com/v3/places/{}/photos?sort=POPULAR&limit=10" OSM_ENDPOINT = "https://www.openstreetmap.org/?mlat={}&mlon={}&zoom=15&layers=M" @@ -35,6 +36,26 @@ def query_poi(search, latitude, longitude): return locations +def query_poi_by_fsq_id(fsq_id): + url = POI_DETAIL_ENDPOINT.format(fsq_id) + response = requests.get(url, headers=headers) + + poi = json.loads(response.text) + + loc = { + "fsq_id": fsq_id, + "name": poi["name"], + "locality": poi["location"]["locality"] if "locality" in poi["location"] else "", + "region": poi["location"]["region"] if "region" in poi["location"] else "", + "latitude": poi["geocodes"]["main"]["latitude"], + "longitude": poi["geocodes"]["main"]["longitude"], + "osm_url": OSM_ENDPOINT.format(poi["geocodes"]["main"]["latitude"], poi["geocodes"]["main"]["longitude"]) + } + create_or_update_poi(loc) + + return loc + + def get_poi_top_photo(fsq_id): url = POI_PHOTO_ENDPOINT.format(fsq_id) response = requests.get(url, headers=headers) -- cgit v1.2.3