Skip to content

Commit

Permalink
#6 support reply
Browse files Browse the repository at this point in the history
  • Loading branch information
amay077 committed Jan 3, 2025
1 parent 96c0e6d commit 5a0f7c2
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 14 deletions.
44 changes: 43 additions & 1 deletion src/lib/MainContent.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ let posting = false;
let text = loadMessage()?.message ?? '';
let imageDataURLs: string[] = [];
let replyToIdForMastodon = '';
let replyToIdForBluesky = '';
let replyToIdForTwitter = '';
onMount(async () => {
console.log(`onMount`);
Expand Down Expand Up @@ -52,12 +56,19 @@ const post = async () => {
try {
posting = true;
const res = await postToSns(text, imageDataURLs);
const res = await postToSns(text, imageDataURLs, { reply_to_ids: {
mastodon: replyToIdForMastodon,
twitter: replyToIdForTwitter,
bluesky: replyToIdForBluesky,
} });
if (res.errors.length == 0) {
text = '';
onTextChange();
imageDataURLs = [];
replyToIdForMastodon = '';
replyToIdForBluesky = '';
replyToIdForTwitter = '';
alert('投稿しました。');
} else {
alert(`${res.errors.join(', ')}に投稿できませんでした。`);
Expand Down Expand Up @@ -163,6 +174,37 @@ const onVersion = async () => {

</div>

<div class="mt-2 d-flex flex-column align-items-start gap-1">
<label for="message" class="form-label">Reply:</label>

{#if postSettings.mastodon != null && postTo.mastodon}
<div class="d-flex flex-row align-items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="bi bi-mastodon" viewBox="0 0 16 16">
<path d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z"/>
</svg>

<input type="text" placeholder="Toot ID" bind:value={replyToIdForMastodon} />
</div>
{/if}

{#if postSettings.bluesky != null && postTo.bluesky}
<div class="d-flex flex-row align-items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -3.268 64 68.414" width="16" height="16"><path fill="currentColor" d="M13.873 3.805C21.21 9.332 29.103 20.537 32 26.55v15.882c0-.338-.13.044-.41.867-1.512 4.456-7.418 21.847-20.923 7.944-7.111-7.32-3.819-14.64 9.125-16.85-7.405 1.264-15.73-.825-18.014-9.015C1.12 23.022 0 8.51 0 6.55 0-3.268 8.579-.182 13.873 3.805zm36.254 0C42.79 9.332 34.897 20.537 32 26.55v15.882c0-.338.13.044.41.867 1.512 4.456 7.418 21.847 20.923 7.944 7.111-7.32 3.819-14.64-9.125-16.85 7.405 1.264 15.73-.825 18.014-9.015C62.88 23.022 64 8.51 64 6.55c0-9.818-8.578-6.732-13.873-2.745z"/></svg>
<input type="text" placeholder="Post ID" bind:value={replyToIdForBluesky} />
</div>
{/if}

{#if postSettings.twitter != null && postTo.twitter}
<div class="d-flex flex-row align-items-center gap-1">
<svg style="margin-top: -2px;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-twitter" viewBox="0 0 16 16">
<path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"/>
</svg>
<input type="text" placeholder="Tweet ID" bind:value={replyToIdForTwitter} />
</div>
{/if}

</div>

<div class="mt-4">
<ImagePreview
bind:imageDataURLs={imageDataURLs}
Expand Down
60 changes: 47 additions & 13 deletions src/lib/MainContent.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ReplyRef } from "@atproto/api/dist/client/types/app/bsky/feed/post";
import { Config } from "../config";
import { type SettingDataMastodon, type SettingDataBluesky, type SettingDataTwitter, loadPostSetting, type SettingType, loadMessage, savePostSetting } from "./func";
import { BskyAgent, RichText, type AtpSessionData } from "@atproto/api";
Expand Down Expand Up @@ -30,7 +31,11 @@ export async function getApiVersion(): Promise<{ build_at: string, env_ver: stri
}
}

export const postToSns = async (text: string, imageDataURLs: string[]): Promise<{ errors: string[] }> => {
export const postToSns = async (text: string, imageDataURLs: string[], options: { reply_to_ids: {
mastodon: string,
bluesky: string,
twitter: string,
}}): Promise<{ errors: string[] }> => {
const errors: string[] = [];

const enableTypes = Array.from(Object.entries(postTo)).filter(([_, v]) => v).map(([k, v]) => (k as SettingType));
Expand All @@ -40,13 +45,13 @@ export const postToSns = async (text: string, imageDataURLs: string[]): Promise<
for (const type of enableTypes) {
switch (type) {
case 'mastodon':
promises.push(postToMastodon(text, imageDataURLs).then((r) => { if (!r) errors.push('Mastodon') }));
promises.push(postToMastodon(text, imageDataURLs, options?.reply_to_ids?.mastodon).then((r) => { if (!r) errors.push('Mastodon') }));
break;
case 'bluesky':
promises.push(postToBluesky(text, imageDataURLs).then((r) => { if (!r) errors.push('Bluesky') }));
promises.push(postToBluesky(text, imageDataURLs, options?.reply_to_ids?.bluesky).then((r) => { if (!r) errors.push('Bluesky') }));
break;
case 'twitter':
promises.push(postToTwritter(text, imageDataURLs).then((r) => { if (!r) errors.push('Twitter') }));
promises.push(postToTwritter(text, imageDataURLs, options?.reply_to_ids?.twitter).then((r) => { if (!r) errors.push('Twitter') }));
break;
}

Expand All @@ -72,7 +77,7 @@ async function url2File(url: string, fileName: string): Promise<File>{
return new File([blob], fileName, {type: blob.type})
}

const postToMastodon = async (text: string, images: string[]): Promise<boolean> => {
const postToMastodon = async (text: string, images: string[], reply_to_id: string): Promise<boolean> => {
try {
const settings = postSettings.mastodon!;
const MASTODON_HOST = settings.server;
Expand Down Expand Up @@ -122,7 +127,7 @@ const postToMastodon = async (text: string, images: string[]): Promise<boolean>
headers: {
'Content-Type': 'text/plain',
},
body: JSON.stringify({ host: MASTODON_HOST, token: settings.token_data.access_token, status, media_ids }),
body: JSON.stringify({ host: MASTODON_HOST, token: settings.token_data.access_token, status, media_ids, reply_to_id }),
});


Expand Down Expand Up @@ -151,16 +156,15 @@ async function findUrlInText(rt: RichText): Promise<string | null> {
return null;
}

const postToBluesky = async (text: string, imageDataURLs: string[]): Promise<boolean> => {
const postToBluesky = async (text: string, imageDataURLs: string[], reply_to_id: string): Promise<boolean> => {
try {
const agent = new BskyAgent({
service: 'https://bsky.social',
});

// resume session


const res = await agent.resumeSession(postSettings.bluesky?.data?.sessionData!);
const sessionRes = await agent.resumeSession(postSettings.bluesky?.data?.sessionData!);
const did = sessionRes?.data?.did;

// refresh tokens
await agent.refreshSession();
Expand Down Expand Up @@ -337,15 +341,45 @@ const postToBluesky = async (text: string, imageDataURLs: string[]): Promise<boo
}
})();


const reply: ReplyRef | undefined = await (async () => {
if ((reply_to_id?.length ?? 0) <= 0) {
return undefined;
}

const uri = `at://${did}/app.bsky.feed.post/${reply_to_id}`;

const r = await agent.getPostThread({ uri });

const th = r?.data?.thread as any;

const cid = th?.post?.cid;
const parent = {
uri,
cid
};

const root = th?.post?.record?.reply?.root ?? parent;

return {
root,
parent
}
})();


const postRecord = {
$type: 'app.bsky.feed.post',
text: rt.text,
facets: rt.facets,
createdAt: new Date().toISOString(),
embed: embedImages ?? embedOgp,
reply
};

await agent.post(postRecord);

const reso = await agent.post(postRecord);
console.log(`FIXME h_oku 後で消す -> postToBluesky -> reso:`, reso);
return true;
} catch (error) {
console.error(`postToBluesky -> error:`, error);
Expand Down Expand Up @@ -407,7 +441,7 @@ const uploadImage = async (content: string /*file: File*/): Promise<string | nul
}
}

const postToTwritter = async (text: string, images: string[]): Promise<boolean> => {
const postToTwritter = async (text: string, images: string[], reply_to_id: string): Promise<boolean> => {
try {
const settings = postSettings.twitter!;
const token = settings.token_data.token;
Expand All @@ -431,7 +465,7 @@ const postToTwritter = async (text: string, images: string[]): Promise<boolean>
headers: {
'Content-Type': 'text/plain',
},
body: JSON.stringify({ token, text, images: imgs }),
body: JSON.stringify({ token, text, images: imgs, reply_to_id }),
});

if (res.ok) {
Expand Down

0 comments on commit 5a0f7c2

Please sign in to comment.