{"flow":{"id":16,"summary":"ActivityPub image to Discord webhook","versions":[35,36,65,117,144],"created_by":"sqwishy","created_at":"2022-08-04T22:21:35.909Z","votes":0,"approved":true,"apps":["discord","activitypub"],"value":{"modules":[{"id":"a","value":{"type":"rawscript","content":"import * as wmill from 'https://deno.land/x/windmill@v1.27.2/mod.ts'\n\nexport async function main(user: string = \"https://chaos.social/users/kernpanik\") {\n  const { orderedItems } = await activity(`${user}/outbox?page=true`);\n\n  for (let { object } of orderedItems) {\n    if (typeof object === \"string\")\n      object = await activity(object);\n\n    for (const { mediaType, url } of object.attachment)\n      if (mediaType.startsWith(\"image/\"))\n        return await isNew(url) ? [{ mediaType, url }] : [];\n  }\n\n  return [];\n}\n\nasync function activity(url: string) {\n  const resp = await fetch(url, { headers: { \"accept\": \"application/activity+json\" } });\n  return await resp.json();\n}\n\nasync function isNew(next: string): bool {\n  const last = await wmill.getInternalState() || null;\n  if (last === next) return false;\n  await wmill.setInternalState(next);\n  return true;\n}\n","language":"deno","input_transforms":{"user":{"type":"static","value":"https://chaos.social/users/kernpanik"}}},"stop_after_if":{"expr":"result.length == 0","skip_if_stopped":true}},{"id":"b","value":{"type":"forloopflow","modules":[{"id":"c","value":{"type":"rawscript","content":"import * as wmill from 'https://deno.land/x/windmill@v1.27.2/mod.ts'\nimport { encode } from \"https://deno.land/std@0.150.0/encoding/base64.ts\";\n\nexport async function main(mediaType: string, url: string, discord: string) {\n  const response = await fetch(url);\n  const blob = await response.blob();\n\n  const { webhook_url } = await wmill.getResource(discord);\n  const file = new File([blob], url.split('/').pop(), {type: response.headers.get(\"content-type\")});\n  const form = new FormData();\n  form.set('file', file);\n  await fetch(webhook_url, {method: 'POST', body: form})\n\n  let preview = {};\n  preview[mediaType.split(\"/\").pop()] = encode(await blob.arrayBuffer());\n  return preview;\n}","language":"deno","input_transforms":{"url":{"expr":"`${previous_result.iter.value.url}`","type":"javascript"},"discord":{"expr":"`${flow_input.discord}`","type":"javascript"},"mediaType":{"expr":"`${previous_result.iter.value.mediaType}`","type":"javascript"}}}}],"iterator":{"expr":"result","type":"javascript"},"skip_failures":true},"input_transforms":{}}]},"schema":{"type":"object","$schema":"https://json-schema.org/draft/2020-12/schema","required":["discord"],"properties":{"user":{"type":"string","format":"","default":"https://chaos.social/users/kernpanik","description":""},"discord":{"type":"string","format":"resource-discord_webhook","description":"discord channel webhook"}}},"description":"Fetches the most recent image attachment from an ActivityPub user (like a Mastodon account) and POSTs it to a Discord Channel Webhook","recording":null,"vcreated_at":"2022-12-14T20:01:51.300Z","vcreated_by":"admin","comments":[]}}