又想折腾机器人了
本文最后更新于222 天前,其中的信息可能已经过时,如有错误请发送邮件到2708694612@qq.com

感谢AstrBot和napcat

添加了几个插件,挨个折腾,记录一些遇到的困难,看看人工队和ai队谁的方案能解决问题hhh

先记录一下,ws链接的时候,natcap设置ws://192.168.0.101:6199/ws;Astrbot设置0.0.0.0

一、Pixiv 图片搜索——Refresh Token获取

感谢B站 up Yzenakiki 的教程

Android 端可以使用 Edge/Firefox 浏览器进行操作

  1. 首先安装 URLRedirector 浏览器插件,下载地址:https://github.com/fengyc/URLRedirector
  2. 导入 URLRedirector 在线规则配置,规则地址:https://pixiv.pictures/helper/redirect.json
  3. 安装 Tampermonkey 浏览器插件,下载地址:https://www.tampermonkey.net/index.php
  4. 安装登录工具用户脚本,脚本地址:https://fastly.jsdelivr.net/gh/asadahimeka/pixiv-viewer@master/public/helper/helper.user.js
  5. 在此页面选择 App API (OAuth) 进行登录:https://pixiv.pictures/account/login,会打开 Pixiv 官方登录页面
  6. 登录成功后可在设置(https://pixiv.pictures/setting/others)页面导出 RefreshToken 以供其他软件使用

二、sauceno搜图

sauceno网站登录以后设置api即可 https://saucenao.com/user.php

三、明日方舟抽卡查询

很莫名其妙,装了以后绑定token报错,尝试修改源代码无果,重装了一遍好了hhh

四、赛博试管插件

作者引用了meme_generator的模板,但是名字貌似有更新,从my_friend变成了my_friend_say

所以写了个判断,这两个都检测一下,没有my_friend就用my_friend_say

meme = next((m for m in get_memes() if m.key in [“my_friend”, “my_friend_say”]), None)

随后出现了无法获取meme版本的错误,经过ai分析meme_generator源码后,思路如下:

meme_generator 没有 from_pilopencoreutils.load_image 这些接口。它唯一接受的 images 元素是 MemeImage(name: str, data: bytes) —— 必须是裸 bytes + 名字。把头像 bytes 直接包成 MemeImage,不要任何 PIL 转换。

决定直接固定meme的版本,最终智能队获胜(恭喜kimi打败了ds和gpt)

import asyncio
from astrbot import logger
from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_message_event import (
    AiocqhttpMessageEvent,
)
from .utils import (
    get_avatar,
    get_reply_text,
    get_replyer_id,
    get_user_name,
)


async def generate_meme(event: AiocqhttpMessageEvent) -> bytes | None:
    """聊天记录转表情包(my_friend 模板)"""

    # 1. 收集素材,任何一步失败直接返回 None
    reply_text = get_reply_text(event)
    if not reply_text:
        return None

    replyer_id = get_replyer_id(event)
    if not replyer_id:
        return None

    avatar = await get_avatar(replyer_id)
    if not avatar:
        return None

    name = await get_user_name(
        client=event.bot,
        group_id=int(event.get_group_id()),
        user_id=int(replyer_id),
    )

    # 2. 动态导入 meme_generator,失败直接返回
    try:
        from meme_generator import get_memes
    except ImportError:
        logger.error("未安装 meme_generator")
        return None

    meme = next((m for m in get_memes() if m.key == "my_friend_say"), None)
    if not meme:
        logger.error("未找到 my_friend 模板")
        return None

    # 3. 根据版本号决定调用方式
    # 3. 不再尝试读版本,默认使用新版 API
    __version__ = "0.3.0"          # 手动指定
    if tuple(map(int, __version__.split("."))) <= (0, 2, 0):
        try:
            from meme_generator.utils import run_sync

            image_io = await run_sync(meme)(
                images=[avatar],
                texts=[reply_text],
                args={"name": name},
            )
            return image_io.getvalue()
        except Exception as e:
            logger.exception(f"meme 生成失败: {e}")
            return None
    else:  # 统一走新版入口
        try:
            from meme_generator import Image as MemeImage

            # 直接包成 MemeImage
            meme_images = [MemeImage(name, avatar)]
            image = await asyncio.to_thread(
                meme.generate,
                images=meme_images,
                texts=[reply_text],  
                options={},
            )
            return image if isinstance(image, bytes) else image.getvalue()
        except Exception as e:
            logger.exception(f"meme 生成失败: {e}")
            return None

五、doro表情包插件

需要延长一下获取时间,不然就算是三次申请api的机会可能都获取不到

from httpx import Timeout

timeout = Timeout(10.0, connect=15.0)   # 连接超时 15 s,读/写超时 10 s
async with httpx.AsyncClient(timeout=timeout) as client:
    response = await client.get(api_url)

六、防撤回插件

气得要死,可能是我太笨,不知道为啥作者只告诉了群组的三段式type,私人聊天的没告诉,一顿乱查,好歹是找到了。以及记得如果napcat和astrbot是部署了两个docker,设置一个共享文件夹连通两个docker的访问文件。

root@astrbot:/AstrBot# cat /AstrBot/astrbot/core/platform/message_type.py
from enum import Enum


class MessageType(Enum):
    GROUP_MESSAGE = "GroupMessage"  # 群组形式的消息,也可以用官方的group
    FRIEND_MESSAGE = "FriendMessage"  # 私聊、好友等单聊消息
    OTHER_MESSAGE = "OtherMessage"  # 其他类型的消息,如系统消息等
root@astrbot:/AstrBot# 

### 最终要设置为 aiocqhttp:FriendMessage:qq_id 这种格式才可以

七、JM下载插件

头一次走端口代理,才知道端口是20171,我是个傻berber,v2raya竟然还有个端口分享的按钮,点了这个才能通过特定的端口转发,服了… …

后续又出现了一个问题,jm一些资源需要登录才能看得到(github上也有不少人遇到了这个问题),根据作者的说法传入avs_cookie就可以模拟登陆,尝试后失败,遂决定修改一点代码传入整个cookie,最后还真搞成功了,人工队win了一局。

class JMClientFactory:
    """JM客户端工厂,负责创建和管理JM客户端实例"""
    
    def __init__(self, config: CosmosConfig, resource_manager: ResourceManager):
        self.config = config
        self.resource_manager = resource_manager
        self.option = self._create_option()
    
    def _create_option(self):
        """创建JM客户端选项"""
        cookies = {
            c.split("=")[0].strip(): c.split("=", 1)[1].strip()
            for c in self.config.avs_cookie.split(";")
            if "=" in c
        }
        logger.info(f"[DEBUG] 使用的Cookies: {cookies}")
        option_dict = {
            "client": {
                "impl": "html",
                "domain": self.config.domain_list,
                "retry_times": 5,
                "postman": {
                    "meta_data": {
                        "proxies": {"https": self.config.proxy} if self.config.proxy else None,
                        "cookies": cookies,
                        # 添加浏览器模拟的请求头
                        "headers": {
                            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36",
                            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                            "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
                            "Referer": f"https://{self.config.domain_list[0]}/",
                            "Connection": "keep-alive",
                            "Cache-Control": "max-age=0"
                        }
                    }
                }
            },
            "download": {
                "cache": True,
                "image": {
                    "decode": True,
                    "suffix": ".jpg"
                },
                "threading": {
                    "image": self.config.max_threads,
                    "photo": self.config.max_threads
                }
            },
            "dir_rule": {
                "base_dir": self.resource_manager.downloads_dir
            },
            "plugins": {
                "after_album": [
                    {
                        "plugin": "img2pdf",
                        "kwargs": {
                            "pdf_dir": self.resource_manager.pdfs_dir,
                            "filename_rule": "Aid"
                        }
                    }
                ]
            }
        }
        yaml_str = yaml.safe_dump(option_dict, allow_unicode=True)
        return jmcomic.create_option_by_str(yaml_str)

八、lolicon插件

源码运行报错

[19:41:23] [Core] [INFO] [core.event_bus:50]: [aiocqhttp] Plumephoenix♥/1106674601: /我要涩涩

Unexpected error saving 69187235_p0.png:

[19:41:43] [Core] [INFO] [respond.stage:212]: AstrBot -> Plumephoenix♥/1106674601: 你怎么这么自私

首先改一下代码输出更多的traceback

import traceback

    async def generate_and_save_image(self, url, filename):
        async with file_lock:
            try:
                async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=15)) as session:
                    async with session.get(url) as response:
                        content = await response.read()  # 异步读取响应内容
                        file_path = os.path.join(self.imgs_folder, filename)
                        async with aiofiles.open(file_path, 'wb') as f:
                            await f.write(content)  # 异步写入文件
                        logger.info(f"Successfully saved image: {filename}")
                        return True
            except aiohttp.ClientError as e:
                logger.error(f"HTTP Error saving {filename}: {str(e)}")
                return False
            except Exception as e:
                logger.error(f"Unexpected error saving {filename}: {repr(e)}\n{traceback.format_exc()}")
                return False

超大工程,根据自己的想法逐步改下,记录一下遇到的问题

  1. 下载超时,经常性的反复下载超时:原因是默认下载original质量的图片,很多图片都太大了(尤其是.png),lolicon仓库有提供更小质量的图片格式,如regular和small等。
  2. 图片来源丢失,有些图片只有original大小的,有的图片是全丢失了(经常是404 not found)
  3. 就算是这样,在线来源也经常抽风,正好我还有点儿存储空间,最终还是想的本地+在线的方式运行

遂采用三步下载的方式,首先是下载regular大小的图片,如果超时下载失败,则执行一次small大小的下载,再失败两种可能,只有original源或者全丢失,最后再尝试一下original源,没有的话就跳过。

以下为五种规格的示例(SFW,请放心打开)

规格地址
originalhttps://i.pixiv.re/img-original/img/2021/06/14/17/25/59/90551655_p0.jpg
regularhttps://i.pixiv.re/img-master/img/2021/06/14/17/25/59/90551655_p0_master1200.jpg
smallhttps://i.pixiv.re/c/540x540_70/img-master/img/2021/06/14/17/25/59/90551655_p0_master1200.jpg
thumbhttps://i.pixiv.re/c/250x250_80_a2/img-master/img/2021/06/14/17/25/59/90551655_p0_square1200.jpg
minihttps://i.pixiv.re/c/48×48/img-master/img/2021/06/14/17/25/59/90551655_p0_square1200.jpg

?暂时保持默认的插件

明日方舟抽卡查询

今天吃什么

今日运势图生成插件

戳一戳插件(多功能需要搭配其他插件)

表情包制作插件

?启用失败的插件

Lolicon API的随机涩图插件:盲猜一手网络原因,获取不到图片缓存

网易云点歌:aiocqhttp.exc message=’消息体无法解析,请检查是否发送了不支持的消息类型体无法解析,请检查是否发送了不支持的消息类型{‘seq’:22}>

摸鱼人日历:api集体失效

版本

v1.1.0 实现了黑名单,优化自动缓存和手动缓存

v1.1.1 尝试使用代理直接走官方的链接,发现不好用,废版本,好像还是有用的,只是没有在日志里面输出成功下载,我还以为不下了来着

v1.1.2 使用pid+页数然后通过pixiv api获取正确的url下载地址(因为lolicon的地址有些因为时间变动而失效)

v1.1.4 补全图库使用pixiv api功能,不是走官方链接,可以获取的到了,但是还在摸索中,同时发现补全图库的请求不能太频繁,不然会被风控;尝试获取所有的日语tag和中文tag,已经发现了gtm-new-work-tag-event-click → 日文标签 + gtm-new-work-translate-tag-event-click → 翻译标签 这两组标签。官方默认是英文翻译。最终可能还是通过外部python爬取的方式,然后又有了一个新的想法,信息貌似能获取收藏数,这样就可以根据收藏数来获取更好看的图片了,如下

{ “create_date”: “创建日期”, “series”: “系列名称”, “page_count”: “图片总数”, “height”: “第一张图片的高度”, “total_view”: “总浏览量”, “id”: “作品ID”, “title”: “作品标题”, “width”: “第一张图片的宽度高度”, “type”: “作品类型,例如illust、manga”, “tags”: [ { “name”: “标签名”, “translated_name”: “标签名翻译” } ], “user”: { “account”: “画师用户名”, “id”: “画师用户ID”, “name”: “画师名称” }, “total_bookmarks”: “收藏总数”, “filename”: “原始文件名称,不含扩展名,如12345_p0”, “page”: “单张图片在所有图片中的顺序,从零开始”

经过查询,建议如果要检索的话把所有的数据都放在一个json文件里面然后内存索引

上一篇
下一篇