First commit
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import config from "@/lib/config";
|
||||
import { EOL } from "node:os";
|
||||
|
||||
type SuccessPosted<T = string> = {
|
||||
ok: true;
|
||||
data: {
|
||||
status: "SUCCESS";
|
||||
id: T;
|
||||
source: "api";
|
||||
deduped: boolean;
|
||||
post: {
|
||||
id: T;
|
||||
content: string;
|
||||
zone: "normal" | string;
|
||||
created_at: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const BASE_URL = "https://collapse.jp/api/v3/bot";
|
||||
|
||||
export const createPost = async (data: {
|
||||
text: string;
|
||||
zone?: "normal" | string;
|
||||
replyTarget?: "all" | string;
|
||||
replyid?: string;
|
||||
images?: [Blob, string][];
|
||||
}, title: string) => {
|
||||
const excessedMessage = "**👉返信に続きがあります。**";
|
||||
|
||||
let lines = data.text.split(EOL);
|
||||
let firstUniqid = "";
|
||||
let count = 0;
|
||||
|
||||
const results: SuccessPosted[] = [];
|
||||
|
||||
while (lines.length > 0) {
|
||||
count++;
|
||||
let currentText = "";
|
||||
|
||||
const maxLength = config.post.maxLength;
|
||||
|
||||
const limit = maxLength - (excessedMessage.length + EOL.length * 2);
|
||||
|
||||
while (lines.length > 0) {
|
||||
const nextLine = lines[0];
|
||||
if (nextLine === undefined)
|
||||
break;
|
||||
|
||||
if (nextLine.length > limit && currentText === "") {
|
||||
const targetLine = lines.shift() || "";
|
||||
currentText = targetLine.slice(0, limit);
|
||||
|
||||
lines.unshift(targetLine.slice(limit));
|
||||
break;
|
||||
}
|
||||
|
||||
const potentialText = currentText ? currentText + EOL + nextLine : nextLine;
|
||||
if (potentialText.length <= limit) {
|
||||
currentText = potentialText;
|
||||
lines.shift();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
currentText = currentText.trimEnd();
|
||||
|
||||
if (lines.length > 0) {
|
||||
currentText += EOL.repeat(2) + excessedMessage;
|
||||
}
|
||||
|
||||
let postedUniqid = "";
|
||||
let success = false;
|
||||
|
||||
for (let attempt = 1; attempt <= config.post.maxRetries; attempt++) {
|
||||
try {
|
||||
const replyid = data.replyid === undefined && firstUniqid !== ""
|
||||
? firstUniqid
|
||||
: data.replyid;
|
||||
const reply = replyid !== undefined
|
||||
? `/${replyid}/reply`
|
||||
: "";
|
||||
const url = new URL(`/posts${reply}`, BASE_URL);
|
||||
|
||||
const body = new FormData();
|
||||
body.append("content", data.text);
|
||||
if (!replyid) {
|
||||
body.append("zone", data.zone ?? "normal");
|
||||
body.append("reply_restriction", data.replyTarget ?? "all");
|
||||
}
|
||||
if (data.images) {
|
||||
data.images.forEach(img => body.append("images", img[0], img[1]));
|
||||
}
|
||||
|
||||
const req = await fetch(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
content: data.text,
|
||||
zone: data.zone ?? "normal",
|
||||
reply_restriction: data.replyTarget ?? "all",
|
||||
}),
|
||||
headers: {
|
||||
"Authorization": `Bearer ${config.mtweet.token}`,
|
||||
},
|
||||
});
|
||||
|
||||
const res = await req.json();
|
||||
|
||||
if (res.ok) {
|
||||
success = true;
|
||||
postedUniqid = res.data.id;
|
||||
results.push(res);
|
||||
break;
|
||||
}
|
||||
|
||||
console.warn(`${title}の投稿に失敗しました (試行 ${attempt}/${config.post.maxRetries}):`, res.error.message);
|
||||
if (attempt < config.post.maxRetries) {
|
||||
const interval = res.error.details?.retry_after
|
||||
? res.error.details.retry_after * 1000
|
||||
: config.post.retryInterval;
|
||||
await new Promise(resolve => setTimeout(resolve, interval));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
console.error(`${title}の全試行が失敗したため、処理を中断します。`);
|
||||
break;
|
||||
}
|
||||
|
||||
if (firstUniqid === "")
|
||||
firstUniqid = postedUniqid;
|
||||
|
||||
console.log(`${title}を投稿(${count}):`, postedUniqid);
|
||||
|
||||
while (lines.length > 0 && lines[0]?.trim() === "") {
|
||||
lines.shift();
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
Reference in New Issue
Block a user