最近我用claude弄了一款从表情包网站获取表情包并发送的插件memeking,刚开始还能正常使用但最近出现了问题,询问ai后说是图库的什么TLS验证问题?
本人没啥编程经验,有这个想法想用ai试一试看能不能成功,没想到刚开始成功了,但过了几天出现问题问了问ai也不知道怎么解决……所以想上论坛看看能不能求助一下![]()
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()
})
}
