diff options
author | clarkzjw <[email protected]> | 2023-02-22 14:01:16 -0800 |
---|---|---|
committer | clarkzjw <[email protected]> | 2023-02-23 12:07:26 -0800 |
commit | ae99c2d7237021e2abb20d4b41a24e0b73028519 (patch) | |
tree | 4c4a2a4d954a51148890d5bf589c938f24633f28 | |
parent | 75b88bc06d354df64c12497330f124392fa7fc57 (diff) | |
download | swarm2fediverse-ae99c2d7237021e2abb20d4b41a24e0b73028519.tar.gz |
bot: support Mastodon OAuth2 login
test callback
test mastodon callback
test callback
clean customwebhook example
bot: test oauth login
test callback url
test callback
-rw-r--r-- | bot.py | 121 | ||||
-rw-r--r-- | callback.py | 15 | ||||
-rw-r--r-- | command.py | 13 | ||||
-rw-r--r-- | config.py | 10 | ||||
-rw-r--r-- | customwebhookexample.py | 193 | ||||
-rw-r--r-- | mastodon.client_id | 2 | ||||
-rw-r--r-- | prompt/string.py | 3 | ||||
-rw-r--r-- | toot.py | 18 |
8 files changed, 171 insertions, 204 deletions
@@ -1,6 +1,26 @@ | |||
1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
2 | 2 | ||
3 | import asyncio | ||
3 | import logging | 4 | import logging |
5 | from dataclasses import dataclass | ||
6 | from http import HTTPStatus | ||
7 | from config import BOT_TOKEN, TELEGRAM_WEBHOOK_URL, HEALTHCHECK_URL, FEDI_LOGIN_CALLBACK_URL, BOT_DOMAIN, BOT_PORT | ||
8 | |||
9 | import uvicorn | ||
10 | from starlette.applications import Starlette | ||
11 | from starlette.requests import Request | ||
12 | from starlette.responses import PlainTextResponse, Response | ||
13 | from starlette.routing import Route | ||
14 | |||
15 | |||
16 | from telegram import Update | ||
17 | from telegram.ext import ( | ||
18 | Application, | ||
19 | CallbackContext, | ||
20 | ContextTypes, | ||
21 | ExtBot, | ||
22 | TypeHandler, | ||
23 | ) | ||
4 | 24 | ||
5 | from telegram.ext import ( | 25 | from telegram.ext import ( |
6 | Application, | 26 | Application, |
@@ -12,6 +32,7 @@ from telegram.ext import ( | |||
12 | ) | 32 | ) |
13 | 33 | ||
14 | from callback import ( | 34 | from callback import ( |
35 | callback_generate_fedi_login_url, | ||
15 | callback_skip_media, | 36 | callback_skip_media, |
16 | callback_location_sharing, | 37 | callback_location_sharing, |
17 | callback_manual_location, | 38 | callback_manual_location, |
@@ -24,10 +45,12 @@ from callback import ( | |||
24 | ) | 45 | ) |
25 | from command import ( | 46 | from command import ( |
26 | start_command, | 47 | start_command, |
48 | fedi_login_command, | ||
27 | cancel_command, | 49 | cancel_command, |
28 | help_command | 50 | help_command |
29 | ) | 51 | ) |
30 | from config import ( | 52 | from config import ( |
53 | FEDI_LOGIN, | ||
31 | WAIT_LOCATION, | 54 | WAIT_LOCATION, |
32 | LOCATION_SEARCH_KEYWORD, | 55 | LOCATION_SEARCH_KEYWORD, |
33 | LOCATION_CONFIRMATION, | 56 | LOCATION_CONFIRMATION, |
@@ -36,21 +59,57 @@ from config import ( | |||
36 | BOT_TOKEN | 59 | BOT_TOKEN |
37 | ) | 60 | ) |
38 | 61 | ||
62 | # Enable logging | ||
39 | logging.basicConfig( | 63 | logging.basicConfig( |
40 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO | 64 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO |
41 | ) | 65 | ) |
42 | logger = logging.getLogger(__name__) | 66 | logger = logging.getLogger(__name__) |
43 | 67 | ||
44 | 68 | ||
45 | def main() -> None: | 69 | @dataclass |
46 | application = Application.builder().token(BOT_TOKEN).build() | 70 | class FediLoginCallbackUpdate: |
71 | code: str | ||
72 | state: int | ||
73 | |||
74 | |||
75 | class FediLoginCallbackContext(CallbackContext[ExtBot, dict, dict, dict]): | ||
76 | @classmethod | ||
77 | def from_update( | ||
78 | cls, | ||
79 | update: object, | ||
80 | application: "Application", | ||
81 | ) -> "FediLoginCallbackContext": | ||
82 | if isinstance(update, FediLoginCallbackUpdate): | ||
83 | return cls(application=application, user_id=update.state) | ||
84 | return super().from_update(update, application) | ||
85 | |||
86 | |||
87 | async def process_oauth_login_callback(update: FediLoginCallbackUpdate, context: FediLoginCallbackContext) -> None: | ||
88 | combined_payloads = update.code | ||
89 | text = "Login success, your code is: {}".format(combined_payloads) | ||
90 | print(text) | ||
91 | print(update.state) | ||
92 | await context.bot.send_message(chat_id=update.state, text=text) | ||
93 | |||
94 | |||
95 | async def main() -> None: | ||
96 | context_types = ContextTypes(context=FediLoginCallbackContext) | ||
97 | # Here we set updater to None because we want our custom webhook server to handle the updates | ||
98 | # and hence we don't need an Updater instance | ||
99 | application = ( | ||
100 | Application.builder().token(BOT_TOKEN).context_types(context_types).build() | ||
101 | ) | ||
47 | 102 | ||
48 | checkin_handler = ConversationHandler( | 103 | checkin_handler = ConversationHandler( |
49 | entry_points=[ | 104 | entry_points=[ |
50 | CommandHandler("start", start_command), | 105 | CommandHandler("start", start_command), |
106 | CommandHandler("login", fedi_login_command), | ||
51 | MessageHandler(filters.LOCATION, callback_location_sharing), | 107 | MessageHandler(filters.LOCATION, callback_location_sharing), |
52 | ], | 108 | ], |
53 | states={ | 109 | states={ |
110 | FEDI_LOGIN: [ | ||
111 | MessageHandler(filters.TEXT & ~filters.COMMAND, callback_generate_fedi_login_url), | ||
112 | ], | ||
54 | WAIT_LOCATION: [ | 113 | WAIT_LOCATION: [ |
55 | MessageHandler(filters.LOCATION, callback_location_sharing), | 114 | MessageHandler(filters.LOCATION, callback_location_sharing), |
56 | ], | 115 | ], |
@@ -74,10 +133,64 @@ def main() -> None: | |||
74 | allow_reentry=True, | 133 | allow_reentry=True, |
75 | ) | 134 | ) |
76 | 135 | ||
136 | # register handlers | ||
77 | application.add_handler(CommandHandler("help", help_command)) | 137 | application.add_handler(CommandHandler("help", help_command)) |
78 | application.add_handler(checkin_handler) | 138 | application.add_handler(checkin_handler) |
79 | application.run_polling() | 139 | application.add_handler(TypeHandler(type=FediLoginCallbackUpdate, callback=process_oauth_login_callback)) |
140 | |||
141 | # Pass webhook settings to telegram | ||
142 | await application.bot.set_webhook(url=f"{BOT_DOMAIN}{TELEGRAM_WEBHOOK_URL}") | ||
143 | |||
144 | # Set up webserver | ||
145 | async def telegram_webhook(request: Request) -> Response: | ||
146 | """Handle incoming Telegram updates by putting them into the `update_queue`""" | ||
147 | await application.update_queue.put( | ||
148 | Update.de_json(data=await request.json(), bot=application.bot) | ||
149 | ) | ||
150 | return Response() | ||
151 | |||
152 | async def fedi_oauth_login_callback(request: Request) -> PlainTextResponse: | ||
153 | """ | ||
154 | Handle incoming webhook updates by also putting them into the `update_queue` if | ||
155 | the required parameters were passed correctly. | ||
156 | """ | ||
157 | try: | ||
158 | code = request.query_params["code"] | ||
159 | state = int(request.query_params.get("state")) | ||
160 | except KeyError: | ||
161 | return PlainTextResponse( | ||
162 | status_code=HTTPStatus.BAD_REQUEST, | ||
163 | content="Mastodon callback request doesn't contain a valid OAuth code", | ||
164 | ) | ||
165 | |||
166 | await application.update_queue.put(FediLoginCallbackUpdate(state=state, code=code)) | ||
167 | return PlainTextResponse("Thank you for login! Now you can close the browser") | ||
168 | |||
169 | async def healthcheck(_: Request) -> PlainTextResponse: | ||
170 | return PlainTextResponse(content="OK") | ||
171 | |||
172 | starlette_app = Starlette( | ||
173 | routes=[ | ||
174 | Route(TELEGRAM_WEBHOOK_URL, telegram_webhook, methods=["POST"]), | ||
175 | Route(HEALTHCHECK_URL, healthcheck, methods=["GET"]), | ||
176 | Route(FEDI_LOGIN_CALLBACK_URL, fedi_oauth_login_callback, methods=["POST", "GET"]), | ||
177 | ] | ||
178 | ) | ||
179 | webserver = uvicorn.Server( | ||
180 | config=uvicorn.Config( | ||
181 | app=starlette_app, | ||
182 | port=BOT_PORT, | ||
183 | use_colors=False, | ||
184 | host="127.0.0.1", | ||
185 | ) | ||
186 | ) | ||
187 | |||
188 | # Run application and webserver together | ||
189 | async with application: | ||
190 | await application.start() | ||
191 | await webserver.serve() | ||
192 | await application.stop() | ||
80 | 193 | ||
81 | 194 | ||
82 | if __name__ == "__main__": | 195 | if __name__ == "__main__": |
83 | main() | 196 | asyncio.run(main()) |
diff --git a/callback.py b/callback.py index 2c69fbd..e7a8308 100644 --- a/callback.py +++ b/callback.py | |||
@@ -57,6 +57,21 @@ async def process_media_group(context: CallbackContext): | |||
57 | await context.bot.send_message(chat_id=chat_id, text=PROMPT_DONE, reply_markup=MAIN_MENU) | 57 | await context.bot.send_message(chat_id=chat_id, text=PROMPT_DONE, reply_markup=MAIN_MENU) |
58 | 58 | ||
59 | 59 | ||
60 | async def callback_generate_fedi_login_url(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: | ||
61 | # generate fedi OAuth login url | ||
62 | home_instance = update.effective_message.text | ||
63 | mastodon_client = Mastodon(client_id=MASTODON_CLIENT_ID_FILE, api_base_url=home_instance) | ||
64 | oauth_url = mastodon_client.auth_request_url(redirect_uris="{}{}".format(BOT_DOMAIN, FEDI_LOGIN_CALLBACK_URL), | ||
65 | scopes=['write:media', 'write:statuses'], | ||
66 | state=update.effective_user.id) | ||
67 | |||
68 | await update.message.reply_text(PROMPT_FEDI_LOGIN, | ||
69 | reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton("Login", url=oauth_url)]]), | ||
70 | parse_mode=ParseMode.MARKDOWN) | ||
71 | |||
72 | return FEDI_LOGIN | ||
73 | |||
74 | |||
60 | async def callback_location_sharing(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: | 75 | async def callback_location_sharing(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: |
61 | if update.message.venue is not None: | 76 | if update.message.venue is not None: |
62 | context.user_data["fsq_id"] = update.message.venue.foursquare_id | 77 | context.user_data["fsq_id"] = update.message.venue.foursquare_id |
@@ -4,6 +4,7 @@ from telegram.error import BadRequest | |||
4 | from telegram.ext import ContextTypes, ConversationHandler | 4 | from telegram.ext import ContextTypes, ConversationHandler |
5 | 5 | ||
6 | from config import * | 6 | from config import * |
7 | from mastodon import Mastodon | ||
7 | 8 | ||
8 | 9 | ||
9 | async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: | 10 | async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: |
@@ -13,6 +14,18 @@ async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> i | |||
13 | return WAIT_LOCATION | 14 | return WAIT_LOCATION |
14 | 15 | ||
15 | 16 | ||
17 | async def fedi_login_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int: | ||
18 | # generate fedi OAuth login url | ||
19 | |||
20 | # mastodon_client = Mastodon(client_id=MASTODON_CLIENT_ID_FILE, api_base_url=TOOT_API_BASE_URL) | ||
21 | # oauth_url = mastodon_client.auth_request_url(redirect_uris="{}/{}".format(BOT_DOMAIN, FEDI_LOGIN_CALLBACK_URL), | ||
22 | # scopes=['write:media', 'write:statuses']) | ||
23 | # | ||
24 | # await update.message.reply_text(PROMPT_FEDI_LOGIN.format(oauth_url), parse_mode=ParseMode.MARKDOWN) | ||
25 | await update.message.reply_text(PROMPT_FEDI_LOGIN_WHERE_IS_INSTANCE, parse_mode=ParseMode.MARKDOWN) | ||
26 | return FEDI_LOGIN | ||
27 | |||
28 | |||
16 | async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: | 29 | async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: |
17 | await update.message.reply_text(PROMPT_HELP) | 30 | await update.message.reply_text(PROMPT_HELP) |
18 | 31 | ||
@@ -20,7 +20,7 @@ DEFAULT_TOOT_VISIBILITY = "private" | |||
20 | 20 | ||
21 | MEDIA_GROUP_TIMEOUT = 3 | 21 | MEDIA_GROUP_TIMEOUT = 3 |
22 | 22 | ||
23 | WAIT_LOCATION, LOCATION_SEARCH_KEYWORD, LOCATION_CONFIRMATION, ADD_MEDIA, ADD_COMMENT = range(5) | 23 | FEDI_LOGIN, WAIT_LOCATION, LOCATION_SEARCH_KEYWORD, LOCATION_CONFIRMATION, ADD_MEDIA, ADD_COMMENT = range(6) |
24 | 24 | ||
25 | MAIN_MENU = ReplyKeyboardMarkup([ | 25 | MAIN_MENU = ReplyKeyboardMarkup([ |
26 | [KeyboardButton(text="Check-in here", request_location=True)], | 26 | [KeyboardButton(text="Check-in here", request_location=True)], |
@@ -42,3 +42,11 @@ class MsgDict(TypedDict): | |||
42 | 42 | ||
43 | KEY_TOOT_STATUS_ID = "toot_status_id" | 43 | KEY_TOOT_STATUS_ID = "toot_status_id" |
44 | KEY_TOOT_STATUS_CONTENT = "toot_status_content" | 44 | KEY_TOOT_STATUS_CONTENT = "toot_status_content" |
45 | |||
46 | MASTODON_CLIENT_ID_FILE = "mastodon.client_id" | ||
47 | |||
48 | TELEGRAM_WEBHOOK_URL = "/checkinbot/webhook" | ||
49 | HEALTHCHECK_URL = "/checkinbot/healthcheck" | ||
50 | FEDI_LOGIN_CALLBACK_URL = "/checkinbot/fedi_login_callback" | ||
51 | BOT_DOMAIN = "https://zjw.social" | ||
52 | BOT_PORT = 30010 | ||
diff --git a/customwebhookexample.py b/customwebhookexample.py deleted file mode 100644 index 328a327..0000000 --- a/customwebhookexample.py +++ /dev/null | |||
@@ -1,193 +0,0 @@ | |||
1 | #!/usr/bin/env python | ||
2 | # This program is dedicated to the public domain under the CC0 license. | ||
3 | # pylint: disable=import-error,wrong-import-position | ||
4 | """ | ||
5 | Simple example of a bot that uses a custom webhook setup and handles custom updates. | ||
6 | For the custom webhook setup, the libraries `starlette` and `uvicorn` are used. Please install | ||
7 | them as `pip install starlette~=0.20.0 uvicorn~=0.17.0`. | ||
8 | Note that any other `asyncio` based web server framework can be used for a custom webhook setup | ||
9 | just as well. | ||
10 | |||
11 | Usage: | ||
12 | Set bot token, url, admin chat_id and port at the start of the `main` function. | ||
13 | You may also need to change the `listen` value in the uvicorn configuration to match your setup. | ||
14 | Press Ctrl-C on the command line or send a signal to the process to stop the bot. | ||
15 | """ | ||
16 | import asyncio | ||
17 | import html | ||
18 | import logging | ||
19 | from dataclasses import dataclass | ||
20 | from http import HTTPStatus | ||
21 | from config import BOT_TOKEN | ||
22 | |||
23 | import uvicorn | ||
24 | from starlette.applications import Starlette | ||
25 | from starlette.requests import Request | ||
26 | from starlette.responses import PlainTextResponse, Response | ||
27 | from starlette.routing import Route | ||
28 | |||
29 | from telegram import __version__ as TG_VER | ||
30 | |||
31 | try: | ||
32 | from telegram import __version_info__ | ||
33 | except ImportError: | ||
34 | __version_info__ = (0, 0, 0, 0, 0) # type: ignore[assignment] | ||
35 | |||
36 | if __version_info__ < (20, 0, 0, "alpha", 1): | ||
37 | raise RuntimeError( | ||
38 | f"This example is not compatible with your current PTB version {TG_VER}. To view the " | ||
39 | f"{TG_VER} version of this example, " | ||
40 | f"visit https://docs.python-telegram-bot.org/en/v{TG_VER}/examples.html" | ||
41 | ) | ||
42 | |||
43 | from telegram import Update | ||
44 | from telegram.constants import ParseMode | ||
45 | from telegram.ext import ( | ||
46 | Application, | ||
47 | CallbackContext, | ||
48 | CommandHandler, | ||
49 | ContextTypes, | ||
50 | ExtBot, | ||
51 | TypeHandler, | ||
52 | ) | ||
53 | |||
54 | # Enable logging | ||
55 | logging.basicConfig( | ||
56 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO | ||
57 | ) | ||
58 | logger = logging.getLogger(__name__) | ||
59 | |||
60 | |||
61 | @dataclass | ||
62 | class WebhookUpdate: | ||
63 | """Simple dataclass to wrap a custom update type""" | ||
64 | |||
65 | user_id: int | ||
66 | payload: str | ||
67 | |||
68 | |||
69 | class CustomContext(CallbackContext[ExtBot, dict, dict, dict]): | ||
70 | """ | ||
71 | Custom CallbackContext class that makes `user_data` available for updates of type | ||
72 | `WebhookUpdate`. | ||
73 | """ | ||
74 | |||
75 | @classmethod | ||
76 | def from_update( | ||
77 | cls, | ||
78 | update: object, | ||
79 | application: "Application", | ||
80 | ) -> "CustomContext": | ||
81 | if isinstance(update, WebhookUpdate): | ||
82 | return cls(application=application, user_id=update.user_id) | ||
83 | return super().from_update(update, application) | ||
84 | |||
85 | |||
86 | async def start(update: Update, context: CustomContext) -> None: | ||
87 | """Display a message with instructions on how to use this bot.""" | ||
88 | url = context.bot_data["url"] | ||
89 | payload_url = html.escape(f"{url}/submitpayload?user_id=<your user id>&payload=<payload>") | ||
90 | text = ( | ||
91 | f"To check if the bot is still running, call <code>{url}/healthcheck</code>.\n\n" | ||
92 | f"To post a custom update, call <code>{payload_url}</code>." | ||
93 | ) | ||
94 | await update.message.reply_html(text=text) | ||
95 | |||
96 | |||
97 | async def webhook_update(update: WebhookUpdate, context: CustomContext) -> None: | ||
98 | """Callback that handles the custom updates.""" | ||
99 | chat_member = await context.bot.get_chat_member(chat_id=update.user_id, user_id=update.user_id) | ||
100 | payloads = context.user_data.setdefault("payloads", []) | ||
101 | payloads.append(update.payload) | ||
102 | combined_payloads = "</code>\n• <code>".join(payloads) | ||
103 | text = ( | ||
104 | f"The user {chat_member.user.mention_html()} has sent a new payload. " | ||
105 | f"So far they have sent the following payloads: \n\n• <code>{combined_payloads}</code>" | ||
106 | ) | ||
107 | await context.bot.send_message( | ||
108 | chat_id=context.bot_data["admin_chat_id"], text=text, parse_mode=ParseMode.HTML | ||
109 | ) | ||
110 | |||
111 | |||
112 | async def main() -> None: | ||
113 | """Set up the application and a custom webserver.""" | ||
114 | url = "https://jinwei.me" | ||
115 | admin_chat_id = 123456 | ||
116 | port = 8000 | ||
117 | |||
118 | context_types = ContextTypes(context=CustomContext) | ||
119 | # Here we set updater to None because we want our custom webhook server to handle the updates | ||
120 | # and hence we don't need an Updater instance | ||
121 | application = ( | ||
122 | Application.builder().token(BOT_TOKEN).updater(None).context_types(context_types).build() | ||
123 | ) | ||
124 | # save the values in `bot_data` such that we may easily access them in the callbacks | ||
125 | application.bot_data["url"] = url | ||
126 | application.bot_data["admin_chat_id"] = admin_chat_id | ||
127 | |||
128 | # register handlers | ||
129 | application.add_handler(CommandHandler("start", start)) | ||
130 | application.add_handler(TypeHandler(type=WebhookUpdate, callback=webhook_update)) | ||
131 | |||
132 | # Pass webhook settings to telegram | ||
133 | await application.bot.set_webhook(url=f"{url}/telegram") | ||
134 | |||
135 | # Set up webserver | ||
136 | async def telegram(request: Request) -> Response: | ||
137 | """Handle incoming Telegram updates by putting them into the `update_queue`""" | ||
138 | await application.update_queue.put( | ||
139 | Update.de_json(data=await request.json(), bot=application.bot) | ||
140 | ) | ||
141 | return Response() | ||
142 | |||
143 | async def custom_updates(request: Request) -> PlainTextResponse: | ||
144 | """ | ||
145 | Handle incoming webhook updates by also putting them into the `update_queue` if | ||
146 | the required parameters were passed correctly. | ||
147 | """ | ||
148 | try: | ||
149 | user_id = int(request.query_params["user_id"]) | ||
150 | payload = request.query_params["payload"] | ||
151 | except KeyError: | ||
152 | return PlainTextResponse( | ||
153 | status_code=HTTPStatus.BAD_REQUEST, | ||
154 | content="Please pass both `user_id` and `payload` as query parameters.", | ||
155 | ) | ||
156 | except ValueError: | ||
157 | return PlainTextResponse( | ||
158 | status_code=HTTPStatus.BAD_REQUEST, | ||
159 | content="The `user_id` must be a string!", | ||
160 | ) | ||
161 | |||
162 | await application.update_queue.put(WebhookUpdate(user_id=user_id, payload=payload)) | ||
163 | return PlainTextResponse("Thank you for the submission! It's being forwarded.") | ||
164 | |||
165 | async def health(_: Request) -> PlainTextResponse: | ||
166 | """For the health endpoint, reply with a simple plain text message.""" | ||
167 | return PlainTextResponse(content="The bot is still running fine :)") | ||
168 | |||
169 | starlette_app = Starlette( | ||
170 | routes=[ | ||
171 | Route("/telegram", telegram, methods=["POST"]), | ||
172 | Route("/healthcheck", health, methods=["GET"]), | ||
173 | Route("/submitpayload", custom_updates, methods=["POST", "GET"]), | ||
174 | ] | ||
175 | ) | ||
176 | webserver = uvicorn.Server( | ||
177 | config=uvicorn.Config( | ||
178 | app=starlette_app, | ||
179 | port=port, | ||
180 | use_colors=False, | ||
181 | host="127.0.0.1", | ||
182 | ) | ||
183 | ) | ||
184 | |||
185 | # Run application and webserver together | ||
186 | async with application: | ||
187 | await application.start() | ||
188 | await webserver.serve() | ||
189 | await application.stop() | ||
190 | |||
191 | |||
192 | if __name__ == "__main__": | ||
193 | asyncio.run(main()) | ||
diff --git a/mastodon.client_id b/mastodon.client_id new file mode 100644 index 0000000..fbddf72 --- /dev/null +++ b/mastodon.client_id | |||
@@ -0,0 +1,2 @@ | |||
1 | wQwT0rHSiGbwet58B2QXFd1-vYvgCBQXjEcjt36UPLc | ||
2 | szIuZqxZsS-XJtLQZLv1_YgClUtUywHRBngbeb9zNXA \ No newline at end of file | ||
diff --git a/prompt/string.py b/prompt/string.py index 9a5a357..a7bf0d1 100644 --- a/prompt/string.py +++ b/prompt/string.py | |||
@@ -4,7 +4,8 @@ PROMPT_START = "Hello, this is `checkin.bot`. \n\n" \ | |||
4 | "Aware of privacy concerns, this bot will not store your location data." \ | 4 | "Aware of privacy concerns, this bot will not store your location data." \ |
5 | "*Be safe and cautious when sharing your real time location on the web.* \n\n" \ | 5 | "*Be safe and cautious when sharing your real time location on the web.* \n\n" \ |
6 | "Start using this bot by sharing your location using Telegram context menu to it." | 6 | "Start using this bot by sharing your location using Telegram context menu to it." |
7 | 7 | PROMPT_FEDI_LOGIN_WHERE_IS_INSTANCE = "Where is your home instance in the Fediverse? (e.g. `https://mastodon.social`)" | |
8 | PROMPT_FEDI_LOGIN = "Please login to your Fediverse account by clicking the link below:" | ||
8 | PROMPT_CHOOSE_ACTION = "Use bot keyboard to choose an action" | 9 | PROMPT_CHOOSE_ACTION = "Use bot keyboard to choose an action" |
9 | PROMPT_ADD_COMMENT = "You can continue adding comments, or press skip" | 10 | PROMPT_ADD_COMMENT = "You can continue adding comments, or press skip" |
10 | PROMPT_ADD_MEDIA = "You can continue adding photos, or press skip" | 11 | PROMPT_ADD_MEDIA = "You can continue adding photos, or press skip" |
@@ -1,12 +1,20 @@ | |||
1 | from mastodon import Mastodon | 1 | from mastodon import Mastodon |
2 | 2 | ||
3 | from config import TOOT_API_BASE_URL, TOOT_CLIENT_SECRET, TOOT_ACCESS_TOKEN, TOOT_CLIENT_ID | 3 | from config import TOOT_API_BASE_URL, TOOT_CLIENT_SECRET, TOOT_ACCESS_TOKEN, TOOT_CLIENT_ID, MASTODON_CLIENT_ID_FILE |
4 | 4 | ||
5 | ''' | 5 | ''' |
6 | https://mastodonpy.readthedocs.io/en/stable/index.html | 6 | https://mastodonpy.readthedocs.io/en/stable/index.html |
7 | ''' | 7 | ''' |
8 | 8 | ||
9 | mastodon_client = Mastodon(client_id=TOOT_CLIENT_ID, | 9 | mastodon_client = Mastodon(client_id=MASTODON_CLIENT_ID_FILE, |
10 | client_secret=TOOT_CLIENT_SECRET, | 10 | api_base_url=TOOT_API_BASE_URL) |
11 | api_base_url=TOOT_API_BASE_URL, | 11 | |
12 | access_token=TOOT_ACCESS_TOKEN) | 12 | url = mastodon_client.auth_request_url(redirect_uris="https://zjw.social/checkinbot/fedi_login_callback", scopes=['write:media', 'write:statuses']) |
13 | print(url) | ||
14 | |||
15 | # mastodon_client.log_in( | ||
16 | # username="[email protected]", | ||
17 | # code='2RnDpj9lMGLWuIeppl-Cghy-iwSXzlJFWU6mQaKYD9o', | ||
18 | # # redirect_uri="urn:ietf:wg:oauth:2.0:oob", | ||
19 | # scopes=['write:media', 'write:statuses'] | ||
20 | # ) | ||