AI开发的插件求助?

最近我用claude弄了一款从表情包网站获取表情包并发送的插件memeking,刚开始还能正常使用但最近出现了问题,询问ai后说是图库的什么TLS验证问题?

本人没啥编程经验,有这个想法想用ai试一试看能不能成功,没想到刚开始成功了,但过了几天出现问题问了问ai也不知道怎么解决……所以想上论坛看看能不能求助一下:pray:

import { Context, Schema, h } from 'koishi'
import * as https from 'https'

export const name = 'sticker-pack'

export const inject = ['http']

export const usage = `
## 表情包插件

- 发送"柴郡"获取随机柴郡表情包
- 发送"doro"获取随机Doro表情包
- 发送"猪猪"获取随机猪猪表情包
- 小心被乱入哦~
`

export interface Config {
  timeout: number
  cheshireKeyword: string
  doroKeyword: string
  pigKeyword: string
  intrudeChance: number
}

export const Config: Schema<Config> = Schema.object({
  cheshireKeyword: Schema.string().default('柴郡'),
  doroKeyword: Schema.string().default('doro'),
  pigKeyword: Schema.string().default('猪猪'),
  timeout: Schema.number().default(30000),
  intrudeChance: Schema.number().default(20).min(0).max(100),
})

// 柴郡/Doro API 响应
interface StickerResponse {
  success: boolean
  sticker: {
    id: string
    url: string
    description: string
    isGif: boolean
  }
}

// 猪猪图片对象
interface PigImage {
  id: string
  title: string
  filename: string
  thumbnail: string
  image_type: string
  duration: string
  view_count: number
  download_count: number
}

// 猪猪 API 响应
interface PigResponse {
  images: PigImage[]
}

// 统一的表情包结果
interface StickerResult {
  url: string
  description: string
  isGif: boolean
}

// API 配置
const STICKER_APIS = {
  cheshire: { 
    name: '柴郡',
    type: 'standard' as const,
    api: 'https://www.cheshire.asia/api/random-sticker',
  },
  doro: { 
    name: 'Doro',
    type: 'standard' as const,
    api: 'https://www.doro.asia/api/random-sticker',
  },
  pig: { 
    name: '猪猪',
    type: 'pig' as const,
    api: 'https://www.pighub.top/api/all-images',
    baseUrl: 'https://www.pighub.top',
  },
}

type StickerType = keyof typeof STICKER_APIS

export function apply(ctx: Context, config: Config) {
  const logger = ctx.logger('sticker-pack')

  function shouldIntrude(): boolean {
    return Math.random() * 100 < config.intrudeChance
  }

  // 下载图片(跳过SSL验证)
  function downloadImage(imageUrl: string): Promise<Buffer> {
    return new Promise((resolve, reject) => {
      // URL 编码处理中文
      const encodedUrl = encodeURI(imageUrl)
      
      const options = {
        rejectUnauthorized: false,
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
          'Referer': 'https://www.pighub.top/',
          'Accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
        },
      }

      https.get(encodedUrl, options, (res) => {
        if (res.statusCode === 301 || res.statusCode === 302) {
          const redirectUrl = res.headers.location
          if (redirectUrl) {
            downloadImage(redirectUrl).then(resolve).catch(reject)
            return
          }
        }

        if (res.statusCode !== 200) {
          reject(new Error(`HTTP ${res.statusCode}`))
          return
        }

        const chunks: Buffer[] = []
        res.on('data', (chunk) => chunks.push(chunk))
        res.on('end', () => resolve(Buffer.concat(chunks)))
        res.on('error', reject)
      }).on('error', reject)
    })
  }

  // 获取柴郡/Doro表情包
  async function fetchStandardSticker(apiUrl: string): Promise<StickerResult | null> {
    try {
      const res = await ctx.http.get(apiUrl, { timeout: config.timeout }) as StickerResponse
      if (res?.success && res.sticker?.url) {
        return {
          url: res.sticker.url,
          description: res.sticker.description || '',
          isGif: res.sticker.isGif || false,
        }
      }
      return null
    } catch (e) {
      logger.warn('API 请求失败:', e)
      return null
    }
  }

  // 获取猪猪表情包
  async function fetchPigSticker(): Promise<StickerResult | null> {
    try {
      const pigConfig = STICKER_APIS.pig
      
      logger.info('请求猪猪API:', pigConfig.api)
      
      const response = await ctx.http.get(pigConfig.api, {
        timeout: config.timeout,
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
          'Referer': 'https://www.pighub.top/',
          'Accept': 'application/json',
        },
      }) as PigResponse

      if (!response?.images?.length) {
        logger.warn('猪猪API返回为空')
        return null
      }

      logger.info('获取到图片数量:', response.images.length)

      // 随机选一个图片
      const randomImage = response.images[Math.floor(Math.random() * response.images.length)]
      logger.info('选中图片:', randomImage.title)
      
      // 构建完整的图片 URL
      const imageUrl = `${pigConfig.baseUrl}${randomImage.thumbnail}`
      logger.info('图片URL:', imageUrl)

      // 判断是否为 GIF
      const isGif = randomImage.image_type !== 'static' || 
                    randomImage.filename.toLowerCase().endsWith('.gif')

      return {
        url: imageUrl,
        description: randomImage.title || '',
        isGif,
      }
    } catch (e) {
      logger.error('猪猪API请求失败:', e)
      return null
    }
  }

  // 获取表情包(统一入口)
  async function fetchSticker(type: StickerType): Promise<StickerResult | null> {
    if (type === 'pig') {
      return await fetchPigSticker()
    } else {
      return await fetchStandardSticker(STICKER_APIS[type].api)
    }
  }

  // 图片转 Base64
  async function getImageAsBase64(imageUrl: string, isGif: boolean): Promise<string | null> {
    try {
      logger.info('下载图片:', imageUrl)
      const buffer = await downloadImage(imageUrl)
      const base64 = buffer.toString('base64')
      
      let mimeType = 'image/png'
      const lowerUrl = imageUrl.toLowerCase()
      if (isGif || lowerUrl.includes('.gif')) mimeType = 'image/gif'
      else if (lowerUrl.includes('.jpg') || lowerUrl.includes('.jpeg')) mimeType = 'image/jpeg'
      else if (lowerUrl.includes('.webp')) mimeType = 'image/webp'
      else if (lowerUrl.includes('.png')) mimeType = 'image/png'
      
      logger.info('下载成功,大小:', buffer.length, '字节')
      return `data:${mimeType};base64,${base64}`
    } catch (e) {
      logger.error('图片下载失败:', e)
      return null
    }
  }

  // 随机获取一个其他类型(用于乱入)
  function getRandomOtherType(currentType: StickerType): StickerType {
    const types = Object.keys(STICKER_APIS) as StickerType[]
    const others = types.filter(t => t !== currentType)
    return others[Math.floor(Math.random() * others.length)]
  }

  // 发送表情包
  async function sendSticker(session: any, requestedType: StickerType) {
    const isIntrude = shouldIntrude()
    const actualType = isIntrude ? getRandomOtherType(requestedType) : requestedType
    
    const { name } = STICKER_APIS[actualType]
    const result = await fetchSticker(actualType)

    if (!result?.url) {
      return session.send('获取表情包失败,请稍后再试~')
    }

    const base64Image = await getImageAsBase64(result.url, result.isGif)

    if (!base64Image) {
      return session.send('图片加载失败,请稍后再试~')
    }

    // 构建消息
    const message = []
    
    if (isIntrude) {
      message.push(h.text(`被${name}乱入了!${name}才是表情包之王!\n`))
    }
    
    message.push(h.image(base64Image))
    
    if (!isIntrude && result.description?.trim()) {
      message.push(h.text(`\n${result.description}`))
    }

    await session.send(message)
  }

  // ========== 注册命令 ==========
  
  ctx.command('柴郡', '获取随机柴郡表情包')
    .action(({ session }) => sendSticker(session, 'cheshire'))

  ctx.command('doro', '获取随机Doro表情包')
    .alias('Doro', 'DORO')
    .action(({ session }) => sendSticker(session, 'doro'))

  ctx.command('猪猪', '获取随机猪猪表情包')
    .alias('pig', 'Pig', 'PIG', '🐷')
    .action(({ session }) => sendSticker(session, 'pig'))

  // ========== 关键词触发 ==========
  
  ctx.middleware(async (session, next) => {
    const content = session.content?.trim()
    const contentLower = content?.toLowerCase()
    
    if (content === config.cheshireKeyword) {
      await sendSticker(session, 'cheshire')
      return
    }
    
    if (contentLower === config.doroKeyword.toLowerCase()) {
      await sendSticker(session, 'doro')
      return
    }
    
    if (content === config.pigKeyword || contentLower === 'pig' || content === '🐷') {
      await sendSticker(session, 'pig')
      return
    }
    
    return next()
  })
}

1 个赞

ai 写的代码你找 ai 解决

2 个赞

有一说一,还是挺神奇的,看来现在的 AI 已经能够胜任写简单的 Koishi 插件了

2 个赞

Gemini 3.5Pro 刚出来的时候都说前端已死

1 个赞

gemini 什么时候 3.5 了,我怎么不知道

1 个赞