From 7d1cd23d8227ec7736ee4f3fa13c16cc642ca735 Mon Sep 17 00:00:00 2001 From: Ryan Luu Date: Wed, 5 Feb 2025 13:54:37 -0800 Subject: [PATCH 1/3] Allow multiple webhooks and Instagram accounts Fixes #20 Add support for multiple webhooks and Instagram accounts. * **Parser Updates:** - Update `parser.add_argument` for `instagram_username` to accept multiple usernames. - Update `parser.add_argument` for `discord_webhook_url` to accept multiple URLs. - Update help text to reflect the new functionality. * **Main Logic Updates:** - Update `args.instagram_username` to handle a list of usernames. - Update `args.discord_webhook_url` to handle a list of URLs. - Update `check_for_new_posts` function to iterate over multiple Instagram accounts and webhooks. - Update `send_to_discord` function to handle multiple webhooks. * **Documentation Updates:** - Update `docs/source/usage.rst` to show usage with multiple Instagram usernames and Discord webhook URLs. - Update `docs/source/getting-started.rst` to reflect the new functionality of specifying multiple webhooks or Instagram accounts. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/RyanLua/InstaWebhooks/issues/20?shareId=XXXX-XXXX-XXXX-XXXX). --- docs/source/getting-started.rst | 23 +++++++++ docs/source/usage.rst | 6 +++ src/instawebhooks/__main__.py | 82 +++++++++++++++++---------------- src/instawebhooks/parser.py | 6 ++- 4 files changed, 75 insertions(+), 42 deletions(-) diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst index 9434871..9481341 100644 --- a/docs/source/getting-started.rst +++ b/docs/source/getting-started.rst @@ -76,3 +76,26 @@ Now, whenever the Instagram account `@raenlua` posts a new photo, it will be sen .. image:: _static/webhook-message.png For more information about using InstaWebhooks, see the `usage guide `_. + +Setting up InstaWebhooks for multiple Instagram accounts or webhooks +-------------------------------------------------------------------- + +You can also set up InstaWebhooks to monitor multiple Instagram accounts or send new posts to multiple Discord webhooks. + +Replace ````, ````, etc. with the usernames of the Instagram accounts you want to monitor and ````, ````, etc. with the Discord webhook URLs you want to send new posts to. + +.. code:: console + + $ instawebhooks ... ... + +It should look something like this: + +.. code:: console + + $ instawebhooks raenlua anotheruser https://discord.com/api/webhooks/0123456789/abcdefghijklmnopqrstuvwxyz https://discord.com/api/webhooks/9876543210/zyxwvutsrqponmlkjihgfedcba + +Now, whenever the Instagram accounts `@raenlua` or `@anotheruser` post a new photo, it will be sent to the specified Discord webhooks. + +.. image:: _static/webhook-message-multiple.png + +For more information about using InstaWebhooks with multiple accounts or webhooks, see the `usage guide `_. diff --git a/docs/source/usage.rst b/docs/source/usage.rst index 73d7370..ffe2b62 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -40,6 +40,12 @@ Examples $ instawebhooks -e -c "New post from {owner_name}: {post_url}" +* Send new posts for multiple Instagram usernames and Discord webhook URLs: + +.. code:: console + + $ instawebhooks ... ... + Reference --------- diff --git a/src/instawebhooks/__main__.py b/src/instawebhooks/__main__.py index 09bbab2..c4c2601 100644 --- a/src/instawebhooks/__main__.py +++ b/src/instawebhooks/__main__.py @@ -145,24 +145,25 @@ def format_message(post: Post): async def send_to_discord(post: Post): """Send a new Instagram post to Discord using a webhook""" - webhook = SyncWebhook.from_url(args.discord_webhook_url) + for webhook_url in args.discord_webhook_url: + webhook = SyncWebhook.from_url(webhook_url) - if args.message_content: - format_message(post) + if args.message_content: + format_message(post) - logger.debug("Sending post sent to Discord...") + logger.debug("Sending post sent to Discord...") - if not args.no_embed: - embed, post_image_file, profile_pic_file = await create_embed(post) - webhook.send( - content=args.message_content, - embed=embed, - files=[post_image_file, profile_pic_file], - ) - else: - webhook.send(content=args.message_content) + if not args.no_embed: + embed, post_image_file, profile_pic_file = await create_embed(post) + webhook.send( + content=args.message_content, + embed=embed, + files=[post_image_file, profile_pic_file], + ) + else: + webhook.send(content=args.message_content) - logger.info("New post sent to Discord successfully.") + logger.info("New post sent to Discord successfully.") async def check_for_new_posts(catchup: int = args.catchup): @@ -170,40 +171,41 @@ async def check_for_new_posts(catchup: int = args.catchup): logger.info("Checking for new posts") - posts = Profile.from_username( - Instaloader().context, args.instagram_username - ).get_posts() + for username in args.instagram_username: + posts = Profile.from_username( + Instaloader().context, username + ).get_posts() - since = datetime.now() - until = datetime.now() - timedelta(seconds=args.refresh_interval) + since = datetime.now() + until = datetime.now() - timedelta(seconds=args.refresh_interval) - new_posts_found = False + new_posts_found = False - async def send_post(post: Post): - logger.info("New post found: https://www.instagram.com/p/%s", post.shortcode) - await send_to_discord(post) + async def send_post(post: Post): + logger.info("New post found: https://www.instagram.com/p/%s", post.shortcode) + await send_to_discord(post) - if catchup > 0: - logger.info("Sending last %s posts on startup...", catchup) - posts_to_send: List[Post] = [] - for post in takewhile(lambda _: catchup > 0, posts): - posts_to_send.append(post) - catchup -= 1 + if catchup > 0: + logger.info("Sending last %s posts on startup...", catchup) + posts_to_send: List[Post] = [] + for post in takewhile(lambda _: catchup > 0, posts): + posts_to_send.append(post) + catchup -= 1 - # Reverse the posts to send oldest first - for post in reversed(posts_to_send): + # Reverse the posts to send oldest first + for post in reversed(posts_to_send): + await send_post(post) + sleep(2) # Avoid 30 requests per minute rate limit + + for post in takewhile( + lambda p: p.date > until, dropwhile(lambda p: p.date > since, posts) + ): + new_posts_found = True await send_post(post) sleep(2) # Avoid 30 requests per minute rate limit - for post in takewhile( - lambda p: p.date > until, dropwhile(lambda p: p.date > since, posts) - ): - new_posts_found = True - await send_post(post) - sleep(2) # Avoid 30 requests per minute rate limit - - if not new_posts_found: - logger.info("No new posts found.") + if not new_posts_found: + logger.info("No new posts found.") def main(): diff --git a/src/instawebhooks/parser.py b/src/instawebhooks/parser.py index 4ff7d2d..ad3ae94 100644 --- a/src/instawebhooks/parser.py +++ b/src/instawebhooks/parser.py @@ -33,15 +33,17 @@ def closure_check_regex(arg_value: str): login_group = parser.add_mutually_exclusive_group() parser.add_argument( "instagram_username", - help="the Instagram username to monitor for new posts", + help="the Instagram usernames to monitor for new posts", type=regex(r"^[a-zA-Z_](?!.*?\.{2})[\w.]{1,28}[\w]$"), + nargs='+', ) parser.add_argument( "discord_webhook_url", - help="the Discord webhook URL to send new posts to", + help="the Discord webhook URLs to send new posts to", type=regex( r"^.*(discord|discordapp)\.com\/api\/webhooks\/([\d]+)\/([a-zA-Z0-9_.-]*)$" ), + nargs='+', ) logging_group.add_argument( "-q", "--quiet", help="hide all logging", action="store_true" From 23776ec672182ba66168515c10affe9d771ec755 Mon Sep 17 00:00:00 2001 From: Ryan Luu Date: Wed, 5 Feb 2025 14:15:17 -0800 Subject: [PATCH 2/3] Remove outdated image and usage guide link --- docs/source/getting-started.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst index 9481341..293548e 100644 --- a/docs/source/getting-started.rst +++ b/docs/source/getting-started.rst @@ -95,7 +95,3 @@ It should look something like this: $ instawebhooks raenlua anotheruser https://discord.com/api/webhooks/0123456789/abcdefghijklmnopqrstuvwxyz https://discord.com/api/webhooks/9876543210/zyxwvutsrqponmlkjihgfedcba Now, whenever the Instagram accounts `@raenlua` or `@anotheruser` post a new photo, it will be sent to the specified Discord webhooks. - -.. image:: _static/webhook-message-multiple.png - -For more information about using InstaWebhooks with multiple accounts or webhooks, see the `usage guide `_. From dd28ac90480dc5bc32fb26589a23f1363b5ef3e9 Mon Sep 17 00:00:00 2001 From: Ryan Luu Date: Wed, 5 Feb 2025 22:19:10 +0000 Subject: [PATCH 3/3] Fix formatting Signed-off-by: GitHub --- src/instawebhooks/__main__.py | 14 +++++++------- src/instawebhooks/parser.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/instawebhooks/__main__.py b/src/instawebhooks/__main__.py index c4c2601..d9b76cf 100644 --- a/src/instawebhooks/__main__.py +++ b/src/instawebhooks/__main__.py @@ -171,18 +171,18 @@ async def check_for_new_posts(catchup: int = args.catchup): logger.info("Checking for new posts") - for username in args.instagram_username: - posts = Profile.from_username( - Instaloader().context, username - ).get_posts() + since = datetime.now() + until = datetime.now() - timedelta(seconds=args.refresh_interval) - since = datetime.now() - until = datetime.now() - timedelta(seconds=args.refresh_interval) + for username in args.instagram_username: + posts = Profile.from_username(Instaloader().context, username).get_posts() new_posts_found = False async def send_post(post: Post): - logger.info("New post found: https://www.instagram.com/p/%s", post.shortcode) + logger.info( + "New post found: https://www.instagram.com/p/%s", post.shortcode + ) await send_to_discord(post) if catchup > 0: diff --git a/src/instawebhooks/parser.py b/src/instawebhooks/parser.py index ad3ae94..8365f7d 100644 --- a/src/instawebhooks/parser.py +++ b/src/instawebhooks/parser.py @@ -35,7 +35,7 @@ def closure_check_regex(arg_value: str): "instagram_username", help="the Instagram usernames to monitor for new posts", type=regex(r"^[a-zA-Z_](?!.*?\.{2})[\w.]{1,28}[\w]$"), - nargs='+', + nargs="+", ) parser.add_argument( "discord_webhook_url", @@ -43,7 +43,7 @@ def closure_check_regex(arg_value: str): type=regex( r"^.*(discord|discordapp)\.com\/api\/webhooks\/([\d]+)\/([a-zA-Z0-9_.-]*)$" ), - nargs='+', + nargs="+", ) logging_group.add_argument( "-q", "--quiet", help="hide all logging", action="store_true"