def _load_config(services_config): for sv_config in services_config: sv.logger.debug(sv_config) service_name = sv_config["service_name"] enable_on_default = sv_config.get("enable_on_default", False) broadcast_tag = sv_config.get("broadcast_tag", None) users_config = sv_config["users"] sv_spider_list = [] for user_config in users_config: wb_spider = WeiboSpider(user_config) sv_spider_list.append(wb_spider) alias_list = user_config.get("alias", []) for alias in alias_list: if alias in alias_dic: raise DuplicateError(f"Alias {alias} is duplicate") alias_dic[alias] = { "service_name": service_name, "user_id": wb_spider.get_user_id() } subService = Service(service_name, enable_on_default=enable_on_default) subr_dic[service_name] = { "service": subService, "spiders": sv_spider_list, "broadcast_tag": BroadcastTag.parse(broadcast_tag) }
img_dir = os.path.join(os.path.expanduser(kokkoro.config.RES_DIR), 'img', 'meme') img = [] img_name = [] def load_images(): global img, img_name, img_dir img = os.listdir(img_dir) img_name = [''.join(s.split('.')[:-1]) for s in img] load_images() sv = Service('meme-generator') _meme = ['表情包', '表情'] show_meme_prefix = join_iterable(_meme, ['列表']) + join_iterable( ['查看'], _meme) + ('meme-list', ) @sv.on_fullmatch(show_meme_prefix) async def show_memes(bot: KokkoroBot, event: EventInterface): msg = "当前表情有:" for meme in img_name: msg += "\n" + meme await bot.kkr_send(event, msg, at_sender=True) _refresh = ['刷新', '更新']
from .. import sv, cb_cmd _time_limit = 30*60 _lmt = FreqLimiter(_time_limit) b_constellations = ["摩羯","水瓶","双鱼","白羊","金牛","双子","巨蟹","狮子","处女","天秤","天蝎","射手"] #国服的(预测) background1 = R.img('priconne/公会离职报告模板.jpg') background2 = R.img('priconne/公会本期报告模板.jpg') REPORT_RESIGN = 0 REPORT_NORMAL = 1 REPORT_UNDECLARED = -1 sv = Service('clanbattle-report') @sv.on_fullmatch(('离职报告', 'retire-report')) async def send_resign_report(bot:KokkoroBot, event:EventInterface): await send_report(bot, event, type=REPORT_RESIGN) @sv.on_fullmatch(('会战报告', 'clanbattle-report')) async def send_normal_report(bot:KokkoroBot, event:EventInterface): await send_report(bot, event, type=REPORT_NORMAL) async def send_report(bot:KokkoroBot, event:EventInterface, type=REPORT_UNDECLARED): if type not in (REPORT_RESIGN,REPORT_NORMAL): await bot.kkr_send(event, "类型错误!", at_sender=True) return
from kokkoro.service import Service from kokkoro import priv from kokkoro.common_interface import KokkoroBot, EventInterface sv = Service('kkr-status', use_priv=priv.NORMAL, manage_priv=priv.SUPERUSER, enable_on_default=True, visible=False) @sv.on_fullmatch(('usage', '使用情况'), only_to_me=True) async def broadcast(bot: KokkoroBot, ev: EventInterface): groups = bot.get_groups() num_g = len(groups) num_m = 0 for g in groups: members = g.get_members() num_m = num_m + len(members) num_m = num_m - num_g msg = f'目前 KokkoroBot 正在为 {num_g} 个群组共 {num_m} 个用户服务 0x0' await bot.kkr_send(ev, msg)
import os import random import json from collections import defaultdict from kokkoro import priv, util, R from kokkoro.service import Service from kokkoro.common_interface import * from kokkoro.util import DailyNumberLimiter, concat_pic, pic2b64, silence from .. import chara from .gacha import Gacha sv = Service('gacha') POOL = ('MIX', 'JP', 'TW', 'BL') DEFAULT_POOL = POOL[0] _pool_config_file = os.path.expanduser('~/.kokkoro/group_pool_config.json') _group_pool = {} try: with open(_pool_config_file, encoding='utf8') as f: _group_pool = json.load(f) except FileNotFoundError as e: sv.logger.warning( 'group_pool_config.json not found, will create when needed.') _group_pool = defaultdict(lambda: DEFAULT_POOL, _group_pool) def dump_pool_config(): with open(_pool_config_file, 'w', encoding='utf8') as f:
from kokkoro.service import Service from kokkoro.common_interface import KokkoroBot, EventInterface import httpx, sys, bs4, itertools, asyncio sv = Service('ark-recruit') url = "https://wiki.biligame.com/arknights/公开招募工具" worker_infos = list() all_tags = set() def parse_data(data): _worker_infos = [] _all_tags = {"资深", "高级资深"} bsObj = bs4.BeautifulSoup(data, "html.parser") details = bsObj.findAll("div", {"class": "contentDetail"}) for detail in details: if u"公开招募" not in detail.attrs["data-param1"]: continue name = detail.find("a").attrs["title"].strip() profes = detail.attrs["data-param1"].split(",")[0].strip() #sex = detail.attrs["data-param1"].split(",")[1].strip() star = int(detail.attrs["data-param2"].strip()) tags = set() if star == 5: tags.add("资深") elif star == 6: tags.add("高级资深") for tag in detail.findAll("span", {"class": "tagText"}):
# 公主连接Re:Dive会战管理插件 # clan == クラン == 戰隊(直译为氏族)(CLANNAD的CLAN(笑)) from .argparse import ArgParser from .exception import * from kokkoro.typing import * from kokkoro.common_interface import KokkoroBot, EventInterface from kokkoro.service import Service from kokkoro.util import join_iterable sv = Service('clanbattle') SORRY = 'ごめんなさい!嘤嘤嘤(〒︿〒)' def cb_prefix(cmds): if isinstance(cmds, str): cmds = (cmds, ) return join_iterable(('!', '!'), cmds) def cb_cmd(prefixes, parser: ArgParser) -> Callable: prefixes = cb_prefix(prefixes) if not isinstance(prefixes, Iterable): raise ValueError('`name` of cb_cmd must be `str` or `Iterable[str]`') def deco(func): async def wrapper(bot: KokkoroBot, ev: EventInterface): try: args = parser.parse(ev.get_param().args, ev) except ParseError as e:
import itertools from kokkoro import priv from kokkoro.service import Service from kokkoro.common_interface import * sv = Service('_help_', manage_priv=priv.SUPERUSER, visible=False) HELP_HEADER = ''' ===================== - KokkoroBot使用说明 - ===================== 发送方括号[]内的关键词即可触发 ※功能采取模块化管理,管理员可控制开关 '''.strip() HELP_BOTTOM = ''' ※※该bot是基于HoshinoBot开发的分支版本KokkoroBot的跨平台版本 '''.strip() PRC_HELP = ''' ================== - 公主连接Re:Dive - ================== [/十连] 十连转蛋模拟 [/单抽] 单抽转蛋模拟 [/井] 4w5钻!买定离手! [查看卡池] 模拟卡池&出率 [切换卡池] 更换模拟卡池 [赛马]兰德索尔赛🐎大赛 [@bot 妈] 给主さま盖章章 [怎么拆 妹弓] 后以空格隔开接角色名,查询竞技场解法
import random, itertools from kokkoro.service import Service from kokkoro.common_interface import KokkoroBot, EventInterface from kokkoro import R, priv, util sv = Service('chat', visible=False) @sv.on_fullmatch(('老婆', 'waifu', 'laopo'), only_to_me=True) async def chat_waifu(bot: KokkoroBot, ev: EventInterface): await bot.kkr_send(ev, '?') # ============================================ # @sv.on_keyword(('确实', '有一说一', 'u1s1', 'yysy')) async def chat_queshi(bot: KokkoroBot, ev: EventInterface): cmd = ev.get_param().plain_text if cmd in ['确实.jpg'] or random.random() < 0.05: await bot.kkr_send(ev, R.img('确实.jpg')) _neigui = ['内鬼'] _image_suffix = ['.jpg', '.png', '.gif'] @sv.on_keyword(tuple(_neigui)) async def chat_neigui(bot: KokkoroBot, ev: EventInterface): cmd = ev.get_param().plain_text if cmd in util.join_iterable(_neigui, _image_suffix) or random.random() < 0.05: await bot.kkr_send(ev, R.img('内鬼.png'))
from kokkoro.service import Service from kokkoro.common_interface import KokkoroBot, EventInterface from kokkoro.modules.priconne import chara import kokkoro import os, random from .. import _chara_data from .guess_helper import WinnerJudger, WinningCounter sv = Service('pcr-desc-guess') HINT_LIMIT = 5 DB_PATH = os.path.expanduser('~/.kokkoro/pcr_desc_guess_winning_counter.db') winner_judger = WinnerJudger() def get_cqcode(chara_id): dir_path = os.path.join(os.path.expanduser(hoshino.config.RES_DIR), 'img', 'priconne', 'unit') if not os.path.exists(dir_path): os.makedirs(dir_path) c = chara.fromid(chara_id) cqcode = '' if not c.icon.exist else c.icon.cqcode return c.name, cqcode @sv.on_fullmatch(('猜描述排行榜', '猜描述群排行')) async def description_guess_group_ranking(bot: KokkoroBot, ev: EventInterface):
import random from kokkoro import R from kokkoro.service import Service from kokkoro.common_interface import EventInterface from kokkoro.util import DailyNumberLimiter sv = Service('pcr-login-bonus') lmt = DailyNumberLimiter(1) login_presents = [ '扫荡券×5', '卢币×1000', '普通EXP药水×5', '宝石×50', '玛那×3000', '扫荡券×10', '卢币×1500', '普通EXP药水×15', '宝石×80', '白金转蛋券×1', '扫荡券×15', '卢币×2000', '上级精炼石×3', '宝石×100', '白金转蛋券×1', ] todo_list = [ '找伊绪老师上课', '给宫子买布丁', '和真琴寻找伤害优衣的人', '找镜哥探讨女装', '跟吉塔一起登上骑空艇', '和霞一起调查伤害优衣的人', '和佩可小姐一起吃午饭', '找小小甜心玩过家家', '帮碧寻找新朋友', '去真步真步王国', '找镜华补习数学', '陪胡桃排练话剧', '和初音一起午睡', '成为露娜的朋友', '帮铃莓打扫咲恋育幼院', '和静流小姐一起做巧克力', '去伊丽莎白农场给栞小姐送书', '观看慈乐之音的演出', '解救挂树的队友', '来一发十连', '井一发当期的限定池', '给妈妈买一束康乃馨',
import asyncio from kokkoro.service import Service, BroadcastTag from .spider import * svtw = Service('pcr-news-tw', help_='台服官网新闻') svbl = Service('pcr-news-bili', help_='B服官网新闻') async def news_poller(spider:BaseSpider, sv:Service, bc_tag, LOG_TAG, send=True): if not spider.item_cache: await spider.get_update() sv.logger.info(f'{LOG_TAG}新闻缓存为空,已加载至最新') return news = await spider.get_update() if not news: sv.logger.info(f'未检索到{LOG_TAG}新闻更新') return sv.logger.info(f'检索到{len(news)}条{LOG_TAG}新闻更新!') if send: await sv.broadcast(spider.format_items(news), bc_tag) async def _async_init(): await news_poller(BiliAllSpider, svbl, BroadcastTag.cn_broadcast, 'B服官网') #asyncio.run(_async_init()) @svtw.scheduled_job('cron', minute='*/20', jitter=20) async def sonet_news_poller(): await news_poller(SonetSpider, svtw, BroadcastTag.tw_broadcast, '台服官网') @svbl.scheduled_job('cron', minute='*/20', jitter=20) async def bili_news_poller():
from .weibo import WeiboSpider import kokkoro from kokkoro.service import BroadcastService, BroadcastTag, Service from kokkoro.common_interface import KokkoroBot, EventInterface from kokkoro import priv from kokkoro import R, util from .exception import * lmt = util.FreqLimiter(5) sv = Service('weibo-poller', manage_priv=priv.SUPERUSER, visible=False) def _load_config(services_config): for sv_config in services_config: sv.logger.debug(sv_config) service_name = sv_config["service_name"] enable_on_default = sv_config.get("enable_on_default", False) broadcast_tag = sv_config.get("broadcast_tag", BroadcastTag.default) users_config = sv_config["users"] sv_spider_list = [] for user_config in users_config: wb_spider = WeiboSpider(user_config) sv_spider_list.append(wb_spider) alias_list = user_config.get("alias", []) for alias in alias_list: if alias in alias_dic: raise DuplicateError(f"Alias {alias} is duplicate") alias_dic[alias] = { "service_name": service_name,
from kokkoro.service import Service, BroadcastTag sv9 = Service('pcr-arena-reminder-utc9', enable_on_default=False, help_='背刺时间提醒(UTC+9)') sv8 = Service('pcr-arena-reminder-utc8', enable_on_default=False, help_='背刺时间提醒(UTC+8)') msg = '主人様、准备好背刺了吗?' @sv8.scheduled_job('cron', hour='14', minute='45', misfire_grace_time=60*10) async def pcr_reminder_utc8(): await sv8.broadcast(msg, tag=[BroadcastTag.cn_broadcast, BroadcastTag.tw_broadcast]) @sv9.scheduled_job('cron', hour='13', minute='45', misfire_grace_time=60*10) async def pcr_reminder_utc9(): await sv9.broadcast(msg, tag=BroadcastTag.jp_broadcast)
from typing import Dict, Union from urllib.parse import urljoin from .webmaster import WebMaster from .argparse import ArgParser, ArgHolder, ParseResult from .argparse.argtype import * from . import sv, cb_cmd, cb_prefix from . import cb_cmd from kokkoro import config from kokkoro.common_interface import KokkoroBot, EventInterface from kokkoro.service import Service from kokkoro.priv import ADMIN, SUPERUSER from kokkoro.util import rand_string, add_salt_and_hash svweb = Service('web') @svweb.scheduled_job('cron', hour='5') async def drop_expired_logins(): now = datetime.now() wm = WebMaster() wm.del_login_by_time(now) EXPIRED_TIME = 7 * 24 * 60 * 60 # 7 days LOGIN_AUTH_COOKIE_NAME = 'yobot_login' # this need be same with static/password.js FRONTEND_SALT = '14b492a3-a40a-42fc-a236-e9a9307b47d2'
import kokkoro from kokkoro import R from kokkoro.config import FONT_PATH from kokkoro.config.modules.priconne import luck from kokkoro.service import Service from kokkoro.common_interface import EventInterface, KokkoroBot from kokkoro.util import DailyNumberLimiter sv_help = ''' [抽签|人品|运势|抽凯露签] 随机角色/指定凯露预测今日运势 准确率高达114.514%! '''.strip() #帮助文本 sv = Service('pcr-luck', help_=sv_help) lmt = DailyNumberLimiter(1) #设置每日抽签的次数,默认为1 BASE_LUCK_IMG_DIR = 'priconne/luck' luck_desc = luck.luck_desc luck_type = luck.luck_type async def read_json_config(file): if not os.path.exists(file): raise Exception(f'json config {file} doesn\'t exist') async with aiofiles.open(file, 'r', encoding='utf-8') as f: content = await f.read()
from kokkoro.service import Service, BroadcastTag from kokkoro import R sv9 = Service('pcr-portion-reminder-utc9', enable_on_default=False, help_='药水购买小助手(UTC+9)') sv8 = Service('pcr-portion-reminder-utc8', enable_on_default=False, help_='药水购买小助手(UTC+8)') #msg = "主人様、记得买经验药水~" img = R.img('提醒药水小助手.jpg') @sv8.scheduled_job('cron', hour='0,6,12,18', misfire_grace_time=60 * 10) async def pcr_portion_reminder_utc8(): await sv8.broadcast(img, [BroadcastTag.cn_broadcast, BroadcastTag.tw_broadcast]) @sv9.scheduled_job('cron', hour='23,5,11,17', misfire_grace_time=60 * 10) async def pcr_portion_reminder_utc9(): await sv9.broadcast(img, BroadcastTag.jp_broadcast)
L_cheru = {W_cheru ∪ `\\W`}* 切噜语由切噜词与标点符号连接而成 """ import re, random from itertools import zip_longest from kokkoro.util import escape from kokkoro.service import Service from kokkoro.common_interface import KokkoroBot, EventInterface from kokkoro import R sv = Service('pcr-cherugo', help_=''' [切噜一下] 转换为切噜语 [切噜~♪切啰巴切拉切蹦切蹦] 切噜语翻译 '''.strip()) CHERU_SET = '切卟叮咧哔唎啪啰啵嘭噜噼巴拉蹦铃' CHERU_DIC = {c: i for i, c in enumerate(CHERU_SET)} ENCODING = 'gb18030' rex_split = re.compile(r'\b', re.U) rex_word = re.compile(r'^\w+$', re.U) rex_cheru_word: re.Pattern = re.compile(rf'切[{CHERU_SET}]+', re.U) def grouper(iterable, n, fillvalue=None): args = [iter(iterable)] * n return zip_longest(*args, fillvalue=fillvalue)
import re import time from collections import defaultdict from kokkoro.util import concat_pic, FreqLimiter from kokkoro.service import Service from kokkoro.msg_handler import EventInterface from kokkoro import R, priv import kokkoro from .. import chara sv = Service('pcr-arena', manage_priv=priv.SUPERUSER, enable_on_default=False) from ..chara import Chara from . import arena lmt = FreqLimiter(5) aliases = ('怎么拆', '怎么解', '怎么打', '如何拆', '如何解', '如何打', '怎麼拆', '怎麼解', '怎麼打', 'jjc查询', 'jjc查詢') aliases_b = tuple('b' + a for a in aliases) + tuple('B' + a for a in aliases) aliases_tw = tuple('台' + a for a in aliases) aliases_jp = tuple('日' + a for a in aliases) @sv.on_prefix(aliases) async def arena_query(bot, ev): await _arena_query(bot, ev, region=1)
import re import random from kokkoro.service import Service from kokkoro.common_interface import KokkoroBot, EventInterface sv = Service('dice') async def do_dice(bot: KokkoroBot, ev: EventInterface, num, min_, max_, opr, offset, TIP="的掷骰结果是:"): if num == 0: await bot.kkr_send(ev, '咦?我骰子呢?') return min_, max_ = min(min_, max_), max(min_, max_) rolls = list(map(lambda _: random.randint(min_, max_), range(num))) sum_ = sum(rolls) rolls_str = '+'.join(map(lambda x: str(x), rolls)) if len(rolls_str) > 100: rolls_str = str(sum_) res = sum_ + opr * offset msg = [ f'{TIP}\n', str(num) if num > 1 else '', 'D',
import aiohttp from kokkoro.service import Service from kokkoro.common_interface import KokkoroBot, EventInterface from kokkoro import R, util sv = Service('antiqks', help_='识破骑空士的阴谋') qks_url = ["granbluefantasy.jp"] qksimg = R.img('antiqks.jpg') @sv.on_keyword(qks_url) async def qks_keyword(bot: KokkoroBot, ev: EventInterface): msg = f'骑空士爪巴\n{qksimg}' await bot.kkr_send(ev, msg, at_sender=True) await util.silence(ev, 60) # 有潜在的安全问题 # @sv.on_rex(r'[a-zA-Z0-9\.]{4,12}\/[a-zA-Z0-9]+') async def qks_rex(bot: KokkoroBot, ev: EventInterface): match = ev.get_param().match msg = f'骑空士爪巴远点\n{qksimg}' res = 'http://' + match.group(0) async with aiohttp.TCPConnector(verify_ssl=False) as connector: async with aiohttp.request( 'GET', url=res, allow_redirects=False, connector=connector, ) as resp:
from kokkoro.service import Service sv = Service('pcr-query') from .query import * from .whois import * from .miner import *
# 本模块来自 HoshinoBot 群友 import os import random import json from collections import defaultdict import kokkoro from kokkoro import R from kokkoro.common_interface import EventInterface from kokkoro.service import Service from kokkoro.util import DailyNumberLimiter, concat_pic from .. import chara # '[赛马]兰德索尔赛🐎大赛' sv = Service('pcr-horse') _pool_config_file = os.path.expanduser('~/.kokkoro/group_pool_config.json') _group_pool = {} POOL = ('MIX', 'JP', 'TW', 'BL') DEFAULT_POOL = POOL[0] try: with open(_pool_config_file, encoding='utf8') as f: _group_pool = json.load(f) except FileNotFoundError as e: sv.logger.warning('group_pool_config.json not found, will create when needed.') _group_pool = defaultdict(lambda: DEFAULT_POOL, _group_pool) lmt = DailyNumberLimiter(5)
import os, math, random from kokkoro.service import Service from kokkoro.common_interface import EventInterface, KokkoroBot from kokkoro.modules.priconne import chara, _pcr_data import kokkoro from .guess_helper import WinnerJudger, WinningCounter sv = Service('pcr-avatar-guess') HINT_LIMIT = 3 PIC_SIDE_LENGTH = 25 ONE_TURN_TIME = 20 DB_PATH = os.path.expanduser(f'~/.kokkoro/pcr_avatar_guess_winning_counter.db') BLACKLIST_ID = [ 1000, # unknow chara 1072, 1900, 1908, 1909, 1910, 1911, 1914, 1915, 1916, 1917, 1918, 1919, 1920,
from functools import cmp_to_key from argparse import ArgumentParser from kokkoro import priv from kokkoro.service import Service from kokkoro.common_interface import EventInterface, KokkoroBot PRIV_TIP = f'群员={priv.NORMAL} GameMaster={priv.SUPERUSER}' sv = Service('service_management', use_priv=priv.ADMIN, manage_priv=priv.SUPERUSER, visible=False) @sv.on_prefix(('lssv', '服务列表', '功能列表'), only_to_me=False) async def lssv(bot: KokkoroBot, ev: EventInterface): parser = ArgumentParser() parser.add_argument('-a', '--all', action='store_true') parser.add_argument('-i', '--invisible', action='store_true') parser.add_argument('-g', '--group', type=int, default=0) args = parser.parse_args(ev.get_param().remain) verbose_all = args.all only_hidden = args.invisible if ev.get_author_id() in bot.config.SUPER_USER: gid = args.group or ev.get_group_id() if not gid: await bot.kkr_send(ev, 'Usage: -g|--group <group_id> [-a|--all]') return
from kokkoro.service import Service from kokkoro.common_interface import * sv = Service('pcr-calendar') cn_official_url = 'https://game.bilibili.com/pcr/#p8' @sv.on_rex(r'^\*?([日台国bB])服?(日历|日程|日程表)?$') async def calendar(bot, ev: EventInterface): match = ev.get_param().match server = match.group(1) if server == '台': await bot.kkr_send(ev, "不好意思,台服日程表暂不支持┭┮﹏┭┮", at_sender=True) return elif server == '日': region = 'jp' elif server in ['国', 'b', 'B']: region = 'cn' yobot_url = f'https://tools.yobot.win/calender/#{region}' msg = f'{server}服日程表\nyobot: {yobot_url}' if region == 'cn': msg = f'{msg}\nbilibili: {cn_official_url}' await bot.kkr_send(ev, msg)