From b37418d1b84fbd0f40b093393dc3e17a78cf35f4 Mon Sep 17 00:00:00 2001 From: vsoch Date: Sun, 1 Dec 2024 18:11:55 -0700 Subject: [PATCH] feat: add bluesky Signed-off-by: vsoch --- .github/workflows/post-jobs-slack.yaml | 1 - README.md | 50 ++++++++---- action.yml | 22 ++++- entrypoint.sh | 10 +++ find-updates.py | 108 +++++++++++++++++++------ 5 files changed, 145 insertions(+), 46 deletions(-) diff --git a/.github/workflows/post-jobs-slack.yaml b/.github/workflows/post-jobs-slack.yaml index 7ae4178..8c7f7fe 100644 --- a/.github/workflows/post-jobs-slack.yaml +++ b/.github/workflows/post-jobs-slack.yaml @@ -21,7 +21,6 @@ jobs: deploy: false test: true - - id: multifield_updater name: Job Updater uses: ./ diff --git a/README.md b/README.md index fd92d3b..5d31974 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ The jobs updater is a simple GitHub action application to, given some trigger and a yaml file with a list of jobs (or other links): -``` +```console - name: My Job ... url: https://my-job.org/12345 @@ -80,7 +80,7 @@ jobs: ``` In the above, we will include the url and name fields, and use the url field to determine uniqueness (default). -Given that you have the slack webhook as a secret provided to the action and `deploy_slack` is true, the default `deploy` +Given that you have the slack webhook as a secret provided to the action and `slack_deploy` is true, the default `deploy` variable (to indicate all services) is true and deployment will happen. If you just want to test, then do: ```yaml @@ -113,11 +113,9 @@ aren't necessarily new) then add test: If test is true, deploy will always be set to false. -### Deploy to Twitter +### Deploy to BlueSky -To deploy to Twitter (in addiction to slack) you are required to set `deploy_twitter` -to true, and also define all the needed environment variables in your repository -secrets. +To deploy to BlueSky, you should set `bluesky_deploy` to true, and also define all the needed environment variables in your repository secrets. ```yaml ... @@ -127,15 +125,10 @@ secrets. with: filename: "_data/jobs.yaml" keys: "url,name" - test: false - slack_deploy: true - - # Also deploy to Twitter (all secrets required in repository secrets) - twitter_deploy: true - twitter_api_secret: ${{ secrets.TWITTER_ACCESS_SECRET }} - twitter_api_key: ${{ secrets.TWITTER_ACCESS_TOKEN }} - twitter_consumer_secret: ${{ secrets.TWITTER_CONSUMER_API_SECRET }} - twitter_consumer_key: ${{ secrets.TWITTER_CONSUMER_API_KEY }} + test: false + bluesky_deploy: true + bluesky_password: ${{ secrets.BLUESKY_PASSWORD }} + bluesky_email: ${{ secrets.BLUESKY_EMAIL }} ``` ### Deploy to Mastodon @@ -184,6 +177,30 @@ and then set `deploy_discord` to true, along with adding the webhook to your rep discord_webhook: ${{ secrets.DISCORD_WEBHOOK }} ``` +### Deploy to Twitter + +To deploy to Twitter (in addiction to slack) you are required to set `twitter_deploy` +to true, and also define all the needed environment variables in your repository +secrets. + +```yaml +... + - id: updater + name: Job Updater + uses: rseng/jobs-updater@add/deploy-arg + with: + filename: "_data/jobs.yaml" + keys: "url,name" + test: false + + # Deploy to Twitter (all secrets required in repository secrets) + twitter_deploy: true + twitter_api_secret: ${{ secrets.TWITTER_ACCESS_SECRET }} + twitter_api_key: ${{ secrets.TWITTER_ACCESS_TOKEN }} + twitter_consumer_secret: ${{ secrets.TWITTER_CONSUMER_API_SECRET }} + twitter_consumer_key: ${{ secrets.TWITTER_CONSUMER_API_KEY }} +``` + ## Variables ### Inputs @@ -199,6 +216,9 @@ The following variables are available. You can also look at the [action.yml](act | hashtag | A hashtag to use (defaults to `#Rseng`) | false | #RSEng | | test | Test the updater (ensure there are jobs) | true | false | | deploy | Global deploy across any service set to true? | true | true | +| bluesky_deploy | Deploy to BlueSky? | true | false | +| bluesky_email | BlueSky email | false | unset | +| bluesky_password | BlueSky password | false | unset | | slack_deploy | Deploy to Slack? | true | false | | slack_webhook | Slack webhook to deploy to. | false | unset | | discord_deploy | Deploy to Discord? | true | false | diff --git a/action.yml b/action.yml index ecf53c3..baa4cb0 100644 --- a/action.yml +++ b/action.yml @@ -1,5 +1,5 @@ name: 'Jobs Updater' -description: "The jobs updater will respond on some trigger, and them parse a jobs file for changes, posting to one or more services." +description: "Respond on a trigger and parse a jobs file for changes, posting to one or more services." inputs: filename: description: the filename for the jobs @@ -35,6 +35,7 @@ inputs: slack_webhook: description: Slack webhook to deploy to. required: false + discord_deploy: description: Deploy to Discord? required: true @@ -43,8 +44,18 @@ inputs: description: Discord webhook to deploy to. required: false -# If you want to post to Twitter, all of these credentials are required for a specific user + # If you want to post to BlueSky, both of these are required + bluesky_deploy: + description: Boolean to deploy to BlueSky + required: false + bluesky_password: + description: BlueSky account password + required: false + bluesky_email: + description: BlueSky account email + required: false + # If you want to post to Twitter, all of these credentials are required for a specific user # API keys are typically generated on behalf of a user (at the bottom of the interface) twitter_deploy: description: Deploy to Twitter? @@ -64,7 +75,7 @@ inputs: description: consumer secret generated for the entire app required: false -# If you want to post to Mastodon, these values are required + # If you want to post to Mastodon, these values are required mastodon_deploy: description: Boolean to deploy to Mastodon required: false @@ -91,7 +102,7 @@ runs: steps: - name: Install Python Dependencies run: | - pip install pyyaml requests Mastodon.py + pip install pyyaml requests Mastodon.py atproto pip install git+https://github.com/tweepy/tweepy.git shell: bash @@ -110,6 +121,9 @@ runs: INPUT_DEPLOY: ${{ inputs.deploy }} SLACK_DEPLOY: ${{ inputs.slack_deploy }} SLACK_WEBHOOK: ${{ inputs.slack_webhook }} + BLUESKY_DEPLOY: ${{ inputs.bluesky_deploy }} + BLUESKY_PASSWORD: ${{ inputs.bluesky_password }} + BLUESKY_EMAIL: ${{ inputs.bluesky_email }} TWITTER_DEPLOY: ${{ inputs.twitter_deploy }} TWITTER_API_KEY: ${{ inputs.twitter_api_key }} TWITTER_API_SECRET: ${{ inputs.twitter_api_secret }} diff --git a/entrypoint.sh b/entrypoint.sh index a630946..3738b3b 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -47,6 +47,11 @@ if [[ "${INPUT_TEST}" == "true" ]]; then DEPLOY=false fi +DEPLOY_BLUESKY=false +if [ ! -z ${BLUESKY_EMAIL+x} ] && [ ! -z ${BLUESKY_PASSWORD+x} ] && [[ "${BLUESKY_DEPLOY}" == "true" ]]; then + DEPLOY_BLUESKY=true +fi + # If everything not unset and deploy twitter is true, we deploy! DEPLOY_TWITTER=false if [ ! -z ${TWITTER_API_KEY+x} ] && [ ! -z ${TWITTER_API_SECRET+x} ] && [ ! -z ${TWITTER_CONSUMER_KEY+x} ] && [ ! -z ${TWITTER_CONSUMER_SECRET+x} ] && [[ "${TWITTER_DEPLOY}" == "true" ]]; then @@ -74,6 +79,7 @@ fi # Alert the user everything that will happen printf " Global Deploy: ${DEPLOY}\n" printf "Deploy Mastodon: ${DEPLOY_MASTODON}\n" +printf " Deploy BlueSky: ${DEPLOY_BLUESKY}\n" printf " Deploy Twitter: ${DEPLOY_TWITTER}\n" printf " Deploy Discord: ${DEPLOY_DISCORD}\n" printf " Deploy Slack: ${DEPLOY_SLACK}\n" @@ -111,6 +117,10 @@ if [[ "${DEPLOY_DISCORD}" == "true" ]]; then COMMAND="${COMMAND} --deploy-discord" fi +if [[ "${DEPLOY_BLUESKY}" == "true" ]]; then + COMMAND="${COMMAND} --deploy-bluesky" +fi + echo "${COMMAND}" ${COMMAND} diff --git a/find-updates.py b/find-updates.py index 376652d..cb7d410 100755 --- a/find-updates.py +++ b/find-updates.py @@ -4,17 +4,19 @@ # 1. Reads in a current and changed yaml file # 2. Finds changes between the two # 3. Post them to slack -# 4. Optionally post them to Twitter -# 5. Optionally post them to Mastodon +# 4. Optionally post them to Twitter, Mastodon, Discord, etc. import argparse -import requests -import random import json import os +import random import sys -import yaml + +import requests import tweepy +import yaml +from atproto import Client as BlueskyClient +from atproto import client_utils from mastodon import Mastodon # Shared headers for slack / discord @@ -22,6 +24,28 @@ success_codes = [200, 201, 204] +icons = [ + "⭐ī¸", + "😍ī¸", + "❤ī¸", + "👀ī¸", + "✨ī¸", + "🤖ī¸", + "😎ī¸", + "đŸ’ŧī¸", + "🤩ī¸", + "đŸĨ‘", + "đŸĨŗ", + "🎉", + "😸ī¸", + "đŸ˜ģī¸", + "👉ī¸", + "đŸ•ļī¸", + "đŸ”Ĩī¸", + "đŸ’ģī¸", +] + + def read_yaml(filename): """ Read yaml from file. @@ -114,6 +138,14 @@ def get_parser(): help="deploy to Discord (required webhook URL in environment)", ) + update.add_argument( + "--deploy-bluesky", + dest="deploy_bluesky", + action="store_true", + default=False, + help="deploy to BlueSky (required user/pass in environment)", + ) + update.add_argument( "--deploy-mastodon", dest="deploy_mastodon", @@ -188,6 +220,20 @@ def get_twitter_client(): ) +def get_bluesky_client(): + """ + Get a BlueSky (atproto) client, also ensure all needed envars are provided. + """ + required = [ + "BLUESKY_PASSWORD", + "BLUESKY_EMAIL", + ] + client = BlueskyClient() + envars = get_required_envars(required, "bluesky") + client.login(envars["BLUESKY_EMAIL"], envars["BLUESKY_PASSWORD"]) + return client + + def get_mastodon_client(): """ Get a Mastodon client, requiring a token and base URL. @@ -203,7 +249,7 @@ def get_mastodon_client(): ) -def prepare_post(entry, keys): +def prepare_post(entry, keys, without_url=False): """ Prepare the post. @@ -212,6 +258,9 @@ def prepare_post(entry, keys): post = "" for key in keys: if key in entry: + # For BlueSky, we include the url separately + if key == "url" and without_url: + continue if key == "url": post = post + entry[key] + "\n" else: @@ -233,6 +282,25 @@ def deploy_slack(webhook, message): ) +def deploy_bluesky(client, entry, hashtag): + """ + Deploy to bluesky. We add the job link separately. + """ + tb = client_utils.TextBuilder() + + # Prepare the post, but without the url + post = prepare_post(entry, keys, without_url=True) + choice = random.choice(icons) + message = f"New {hashtag} Job! {choice}\n{post}" + print(message) + + # Add the text to the textbuilder + tb.text(message) + tb.link("CBOR", entry["url"]) + response = client.send_post(tb) + print(f"Posted to bluesky {response.uri}: {response.cid}") + + def deploy_discord(webhook, message): """ Deploy a post to Discord @@ -284,6 +352,11 @@ def help(return_code=0): if args.deploy_mastodon: mastodon_client = get_mastodon_client() + # Deploying to BlueSky? + bluesky_client = None + if args.deploy_bluesky: + bluesky_client = get_bluesky_client() + # Prepare webhooks for slack and mastodon slack_webhook = os.environ.get("SLACK_WEBHOOK") discord_webhook = os.environ.get("DISCORD_WEBHOOK") @@ -331,27 +404,7 @@ def help(return_code=0): matrix = [] - # Format into slack messages - icons = [ - "⭐ī¸", - "😍ī¸", - "❤ī¸", - "👀ī¸", - "✨ī¸", - "🤖ī¸", - "😎ī¸", - "đŸ’ŧī¸", - "🤩ī¸", - "😸ī¸", - "đŸ˜ģī¸", - "👉ī¸", - "đŸ•ļī¸", - "đŸ”Ĩī¸", - "đŸ’ģī¸", - ] - for entry in new: - # Prepare the post post = prepare_post(entry, keys) choice = random.choice(icons) @@ -379,6 +432,9 @@ def help(return_code=0): if args.deploy_twitter and twitter_client: deploy_twitter(twitter_client, newline_message) + if args.deploy_bluesky and bluesky_client: + deploy_bluesky(bluesky_client, entry, args.hashtag) + # If we are instructed to deploy to mastodon and have a client if args.deploy_mastodon and mastodon_client: mastodon_client.toot(status=message)