packages/missgram: fix

This commit is contained in:
2025-12-29 14:06:23 +08:00
parent 8587524732
commit e1879c6625
4 changed files with 138 additions and 35 deletions

View File

@@ -20,6 +20,7 @@ target_compile_features(missgram PRIVATE cxx_std_23)
if(DEFINED MISSGRAM_CONFIG_FILE)
target_compile_definitions(missgram PRIVATE MISSGRAM_CONFIG_FILE="${MISSGRAM_CONFIG_FILE}")
endif()
target_compile_definitions(missgram PRIVATE BIU_LOGGER_SOURCE_ROOT="${CMAKE_CURRENT_SOURCE_DIR}")
install(TARGETS missgram RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
get_property(ImportedTargets DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY IMPORTED_TARGETS)

View File

@@ -2,11 +2,6 @@
namespace missgram
{
void db_write(std::string misskey_note, std::int32_t telegram_message_id);
std::optional<std::int32_t> db_read(std::string misskey_note);
std::optional<std::int32_t> tg_send(std::string text, std::optional<std::int32_t> replyId = {});
struct Config
{
std::string Secret;
@@ -15,4 +10,10 @@ namespace missgram
std::int16_t ServerPort;
std::string dbPassword;
} inline config;
struct File { std::string url; bool is_photo; bool should_hidden; };
void db_write(std::string misskey_note, std::int32_t telegram_message_id);
std::optional<std::int32_t> db_read(std::string misskey_note);
std::optional<std::int32_t> tg_send(std::string text, std::optional<std::int32_t> replyId, std::vector<File> files);
}

View File

@@ -21,47 +21,88 @@ int main()
{
biu::Logger::try_exec([&]
{
log.debug(req.body);
log.debug("{}"_f(req.headers));
if (req.get_header_value("x-misskey-hook-secret") != config.Secret)
throw std::runtime_error("Invalid secret key.");
struct Content
{
std::string type, server;
struct
struct Body
{
struct Note
{
std::string id, text, visibility;
std::optional<std::string> replyId;
std::string id, visibility;
std::optional<std::string> text, replyId;
struct Renote { std::string id; };
std::optional<Renote> renote;
bool localOnly;
struct File { bool isSensitive; std::string url; std::string type; };
std::vector<File> files;
};
std::optional<Note> note;
} body;
};
auto content = YAML::Load(req.body).as<Content>();
log();
// 只考虑公开且允许联合的帖子。
if
(
content.type != "note" // 只转发 note 的情况
content.type != "note" // 只考虑 note 的情况这里note包括了回复、转发、引用
|| !content.body.note // 大概不会发生,但还是判断一下
|| content.body.note->visibility != "public" // 只转发公开的 note
|| content.body.note->visibility != "public" || content.body.note->localOnly // 只转发公开的、允许联合的帖子
|| content.body.note->replyId // 不转发回复
) return;
std::string text = content.body.note->text;
if (content.body.note->renote)
text += "\n🔁 Renote: {}/notes/{}"_f(content.server, content.body.note->renote->id);
std::thread([text, note_id = content.body.note->id]
// 接下来准备要转发的文字内容
std::string text;
std::optional<std::uint32_t> reply_id;
// 如果是转发,则直接写链接
if (!content.body.note->text)
text = "转发帖子: [在铜锣湾查看]({}/notes/{})"_f(content.server, content.body.note->id);
// 否则(引用或普通帖子)
else
{
auto message_id = tg_send(text);
text = *content.body.note->text;
// 如果有引用,则需要查找被引用的帖子是否已经被转发过,若是则直接回复被转发的消息。
// 如果没有被转发过,则附上链接
if (content.body.note->renote)
{
reply_id = db_read(content.body.note->renote->id);
if (!reply_id)
text += "\n引用帖子: [在铜锣湾查看]({}/notes/{})"_f(content.server, content.body.note->renote->id);
}
// 最后附上原贴地址
text += "\n[在铜锣湾查看回复/回应]({}/notes/{})"_f(content.server, content.body.note->id);
}
// 接下来整理要转发的文件
auto files = content.body.note->files | ranges::views::transform([](auto&& file) -> File
{
return File
{
.url = file.url,
.is_photo = file.type.starts_with("image/"),
.should_hidden = file.isSensitive
};
}) | ranges::to_vector;
log();
// 异步发送消息
std::thread([text, note_id = content.body.note->id, reply_id, files]
{
auto message_id = tg_send(text, reply_id, files);
if (message_id) db_write(note_id, *message_id);
}).detach();
// 完成 http 响应
res.status = 200;
res.body = "OK";
log.debug(req.body);
});
});
svr.listen("0.0.0.0", config.ServerPort);

View File

@@ -1,29 +1,89 @@
# include <missgram.hpp>
# include <tgbot/tgbot.h>
std::optional<std::int32_t> missgram::tg_send(std::string text, std::optional<std::int32_t> replyId)
std::optional<std::int32_t> missgram::tg_send
(std::string text, std::optional<std::int32_t> replyId, std::vector<File> files)
{
using namespace biu::literals;
// 准备信息
// 整理要发送的信息
TgBot::Bot bot(config.TelegramBotToken);
std::shared_ptr<TgBot::ReplyParameters> reply;
if (replyId) reply = std::make_shared<TgBot::ReplyParameters>(*replyId);
if (replyId) reply = std::make_shared<TgBot::ReplyParameters>(*replyId, config.TelegramChatId);
auto attachs = files
| ranges::views::transform([&](auto&& file) -> TgBot::InputMedia::Ptr
{
if (file.is_photo)
{
auto pic = std::make_shared<TgBot::InputMediaPhoto>();
pic->media = file.url;
pic->hasSpoiler = file.should_hidden;
return pic;
}
else
{
auto doc = std::make_shared<TgBot::InputMediaDocument>();
doc->media = file.url;
return doc;
}
})
| ranges::to_vector;
// 发送信息,带重试机制
TgBot::Message::Ptr message;
// 多次尝试运行函数直到成功或达到最大尝试次数5次
auto try_run = [&](auto&& func) -> std::optional<std::int32_t>
{
auto retry_delay = 1s;
int attempts = 0;
while (attempts < 5 && !message)
while (attempts < 5)
{
biu::Logger::try_exec([&]
{
message = bot.getApi().sendMessage
(config.TelegramChatId, text, nullptr, reply);
});
if (!message) { std::this_thread::sleep_for(retry_delay); retry_delay *= 2; attempts++; }
TgBot::Message::Ptr message;
biu::Logger::try_exec([&] { message = func(); });
if (message) return message->messageId;
std::this_thread::sleep_for(retry_delay);
retry_delay *= 2;
attempts++;
}
return std::nullopt;
};
// 返回消息 ID
if (message) return message->messageId; else return {};
// 如果没有附件,使用 sendMessage 发送文本消息
if (attachs.empty()) return try_run([&] { return bot.getApi().sendMessage
(
config.TelegramChatId, text, nullptr, reply, nullptr,
"MarkdownV2"
);});
// 如果只有一个附件并且是图片,使用 sendPhoto 发送
else if (attachs.size() == 1 && files[0].is_photo) return try_run([&]
{
return bot.getApi().sendPhoto
(
config.TelegramChatId, files[0].url, text, reply,
nullptr, "MarkdownV2", false, {}, 0, false, files[0].should_hidden
);
});
// 如果有多个附件,使用 sendMediaGroup 分两条消息发送,返回第一条的 id
else
{
auto message = try_run([&] { return bot.getApi().sendMessage
(
config.TelegramChatId, text, nullptr, reply, nullptr,
"MarkdownV2"
);});
if (message)
{
auto message2 = try_run([&] -> TgBot::Message::Ptr
{
auto msg = bot.getApi().sendMediaGroup
(
config.TelegramChatId, attachs, false,
std::make_shared<TgBot::ReplyParameters>(*message, config.TelegramChatId)
);
if (msg.empty() || !ranges::all_of(msg, [](auto&& m) { return bool(m); }))
return nullptr;
else return msg[0];
});
if (!message2) return {};
}
return message;
}
}