├── .env ├── .gitignore ├── Assets ├── 851tegaki_zatsu_normal_0883.ttf ├── DigitalElectronicsTech │ ├── 555.json5 │ ├── CMOS.json5 │ ├── TTL.json5 │ ├── img │ │ ├── 555.png │ │ ├── 555_.png │ │ ├── 74LS138.png │ │ ├── 74LS147.png │ │ ├── 74LS148.png │ │ ├── 74LS151.png │ │ ├── 74LS153.png │ │ ├── 74LS160.png │ │ ├── 74LS161.png │ │ ├── 74LS175.png │ │ ├── 74LS194.png │ │ ├── 74LS283.png │ │ ├── 74LS290.png │ │ ├── 74LS42.png │ │ ├── 74LS48.png │ │ ├── 74LS83.png │ │ ├── 74LS85.png │ │ ├── CC14585.png │ │ └── TG.png │ ├── t.py │ ├── 加法器.json5 │ ├── 半导体存储器.json5 │ ├── 可编程逻辑器件.json5 │ ├── 寄存器.json5 │ ├── 数值比较器.json5 │ ├── 数据选择器.json5 │ ├── 编码器.json5 │ ├── 计数器.json5 │ └── 译码器.json5 ├── E2.png ├── IPlayRhythmGame.png ├── TiMidity++-2.15.0-w32.zip ├── __init__.py ├── answer.jpg ├── autogui │ └── chatgpt.jpg ├── credits.jpg ├── default.png ├── glm6b_api.py ├── kuaikule.png ├── kusa │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 16.png │ ├── 17.png │ ├── 18.png │ ├── 19.png │ ├── 2.jpg │ ├── 20.png │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.png │ ├── 7.jpg │ ├── 8.png │ └── 9.png ├── mhtslm.png ├── morse2zh.json ├── muzukashi.png ├── nya.png ├── random_name │ ├── first.xlsx │ ├── last.xlsx │ └── train.xls ├── sarasa-gothic-ttf-0.12.5 │ ├── sarasa-ui-tc-bold.ttf │ └── sarasa-ui-tc-regular.ttf ├── setofont-ex.ttf ├── setofont.ttf ├── sign │ ├── alpha │ │ ├── origin │ │ │ ├── Bdark.png │ │ │ ├── Blight.png │ │ │ ├── Bplain.png │ │ │ ├── Cdark.png │ │ │ ├── Clight.png │ │ │ ├── Cplain.png │ │ │ ├── Ndark.png │ │ │ ├── Nlight.png │ │ │ └── Nplain.png │ │ └── small │ │ │ ├── Bdark.png │ │ │ ├── Blight.png │ │ │ ├── Bplain.png │ │ │ ├── Cdark.png │ │ │ ├── Clight.png │ │ │ ├── Cplain.png │ │ │ ├── Ndark.png │ │ │ ├── Nlight.png │ │ │ └── Nplain.png │ └── withBG │ │ ├── B1black.png │ │ ├── B1pink.jpg │ │ ├── B1pink.png │ │ └── B1white.png ├── waifusd │ ├── O1.xls │ ├── P1.py │ ├── PW1.py │ ├── chat_tokens.txt │ ├── chathisfilter.py │ ├── cn_cheatsheet.dict │ ├── cn_cheatsheet.xls │ ├── cn_cheatsheet.xlsx │ ├── cn_cheatsheet_dict.pkl │ ├── cn_cheatsheet_list.pkl │ ├── predict.py │ ├── prompts.pickle │ ├── 坏词坏句.txt │ └── 好词好句.txt ├── wish.jpg ├── wish.png ├── zh2morse.json ├── 一种鱼.txt ├── 中药.xlsx ├── 喜报.jpg ├── 柴郡猫猫 │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ ├── 4.jpg │ ├── 5.jpg │ ├── 6.jpg │ └── 7.jpg └── 疯狂星期四 │ ├── photos │ ├── photo_10@13-01-2022_01-46-09.jpg │ ├── photo_11@13-01-2022_01-46-09.jpg │ ├── photo_12@26-05-2022_14-12-55.jpg │ ├── photo_13@26-05-2022_14-13-47.jpg │ ├── photo_1@13-01-2022_01-26-54.jpg │ ├── photo_2@13-01-2022_01-46-09.jpg │ ├── photo_3@13-01-2022_01-46-09.jpg │ ├── photo_4@13-01-2022_01-46-09.jpg │ ├── photo_5@13-01-2022_01-46-09.jpg │ ├── photo_6@13-01-2022_01-46-09.jpg │ ├── photo_7@13-01-2022_01-46-09.jpg │ ├── photo_8@13-01-2022_01-46-09.jpg │ └── photo_9@13-01-2022_01-46-09.jpg │ └── result.json ├── DEV.md ├── Dockerfile ├── Dockerfile-CN ├── LICENSE ├── MathMultiTest.py ├── OS_requirements.txt ├── QUICAdapterMirai.py ├── QUICAdapterTerm.py ├── QUICFaker.py ├── QUICServer.py ├── QUICWorker.py ├── README.md ├── RunCommand.txt ├── Server.py ├── SocketServer.py ├── SocketTerminal.py ├── SocketWorker.py ├── TS1.py ├── TS2.py ├── TaskPoolSender.py ├── TaskPoolWorker.py ├── VendorAdapter7007.py ├── Worker.py ├── __init__.py ├── architechture.sai2 ├── automata_requirements.txt ├── basicutils ├── CONST.py ├── __init__.py ├── algorithms.py ├── applications │ ├── Credit.py │ ├── File.py │ ├── Game.py │ ├── Generator.py │ ├── Math.py │ ├── Player.py │ ├── Spider.py │ ├── String.py │ ├── Test.py │ ├── Translate.py │ ├── Vendor.py │ └── __init__.py ├── chain.py ├── database.py ├── media.py ├── network.py ├── quic.py ├── quine_mccluskey │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── README.md │ ├── assets │ │ └── images │ │ │ ├── circuit.jpg │ │ │ ├── combining.png │ │ │ ├── coverage.png │ │ │ ├── grouping.png │ │ │ ├── solution.png │ │ │ └── win_icon.jpeg │ ├── core │ │ ├── __init__.py │ │ ├── qm │ │ │ ├── __init__.py │ │ │ ├── petrick.py │ │ │ └── qm.py │ │ └── tests │ │ │ ├── __init__.py │ │ │ ├── test_petrick.py │ │ │ └── test_qm.py │ ├── qmccluskey.py │ ├── requirements.txt │ ├── setup.py │ └── usample.txt ├── rpc │ ├── __init__.py │ ├── openai.py │ └── translate.py └── task.py ├── config.py ├── database_migrator.py ├── docker_init.sh ├── fapi ├── G.py ├── MiraiSession.py ├── Sessions.py ├── WebsocketSession.py ├── __init__.py ├── models │ ├── Auth.py │ ├── Base.py │ ├── FileStorage.py │ ├── Player.py │ ├── Routiner.py │ └── __init__.py ├── routers │ ├── __init__.py │ ├── application.py │ ├── auth.py │ ├── convert.py │ └── worker.py └── utils │ ├── jwt.py │ ├── media.py │ └── syscall.py ├── ffmpeg-get-install.sh ├── ffmpeg.conf ├── htmlws.htm ├── install_docker_centos.sh ├── install_docker_debian.sh ├── irori-OSS.py ├── irori_automata.py ├── requirements.txt ├── tasks.py ├── test.py ├── test2.py ├── test3_celery.py ├── test4_celery.py └── testinvoke.py /.env: -------------------------------------------------------------------------------- 1 | PYTHONASYNCIODEBUG=1 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | 3 | .vscode/ 4 | compile_commands.json 5 | *.log 6 | CF/ 7 | credits/ 8 | __pycache__/ 9 | *.pyc 10 | tmp* 11 | temp* 12 | hakushinAVG.txt 13 | authdata 14 | groupLimit.json 15 | cfg.json 16 | cfg.bak 17 | tt.py 18 | ssl/ 19 | cfg.py 20 | celeryconfig.py 21 | 22 | fapi/webcfg.py 23 | 24 | startup_actions.json 25 | 26 | waifusd_auth.pickle 27 | -------------------------------------------------------------------------------- /Assets/851tegaki_zatsu_normal_0883.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/851tegaki_zatsu_normal_0883.ttf -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/555.json5: -------------------------------------------------------------------------------- 1 | { 2 | "555": { 3 | "AN": ["555定时器","定时器"], 4 | "desc": "\ 5 | 用处很多的555定时器\n\ 6 | 内含两个电压比较器,一个基本RS触发器,集电极开路三极管\n\ 7 | 管脚列表:\n\ 8 | 1:GND接地\n\ 9 | 8:Vcc接电源\n\ 10 | 5:Vco参考电压,悬空时参考电压分别为2/3Vcc,1/3Vcc,若接入则为Vco和1/2Vco\n\ 11 | 3:Vo输出端(输出电压低于电源电压1~3V)\n\ 12 | 6:TH,阈值端\n\ 13 | 2:TR',触发端\n\ 14 | 4:MR',复位端\n\ 15 | 7:Vo'放电端\n\ 16 | ", 17 | "img":["555.png","555_.png"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/CMOS.json5: -------------------------------------------------------------------------------- 1 | { 2 | "CMOS": { 3 | "AN": ["CMOS反相器"], 4 | "desc": "\ 5 | CMOS反相器由N沟道MOSFET和P沟道MOSFET互补而成\n\ 6 | 在高频切换高低电平的电路中可能会有不小的动态功耗\n\ 7 | 注意CMOS系电路引脚是不允许悬空的,引脚无论如何接地都视为接0\n\ 8 | 常用参数:\n\ 9 | 电源电位Vdd:CC4000系列可在3~18V内选取\n\ 10 | 高电平电位VH:Vdd\n\ 11 | 低电平电位VL:0V\n\ 12 | 阈值电位:50%Vdd\n\ 13 | 高电平噪声容限VNH = 低电平噪声容限VNL = 30%Vdd\n\ 14 | 平均传输延时:10ns\n\ 15 | " 16 | }, 17 | "TG门":{ 18 | "AN":["TG","传输门","CMOS传输门"], 19 | "desc":"\ 20 | 全称Transmission Gate。\n\ 21 | 特点为导通时Vo=Vi\n\ 22 | 带圈为C'端,当C'=0;C=Vdd时开关导通\n\ 23 | C=0;C'=Vdd时开关断开。\n\ 24 | Vi和Vo可以互换。\n\ 25 | ", 26 | "img":["TG.png"] 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/TTL.json5: -------------------------------------------------------------------------------- 1 | { 2 | "TTL": { 3 | "AN": ["TTL电路","TTL反相器","TTL非门"], 4 | "desc": "\ 5 | 晶体管-晶体管逻辑电路,英文全称:Transistor-Transistor-Logic\n\ 6 | TTL系电路悬空和通过大于10kΩ电阻接地时视为接1;通过小于10kΩ电阻接地视为接0\n\ 7 | TTL反相器常用参数:\n\ 8 | 电源电位Vcc:5V\n\ 9 | 高电平电位VH:3.4V\n\ 10 | 低电平电位VL:0.2V\n\ 11 | 阈值电位1(过此点后线性下降):0.6V\n\ 12 | 阈值电位2(过此点后跳变为低电平):1.4V\n\ 13 | 高电平噪声容限VNH = 低电平噪声容限VNL = 0.4V\n\ 14 | 输入低电平,则向外流出1mA电流\n\ 15 | 输入高电平,则向内流入40μA电流\n\ 16 | 输出高电平,则向外拉出不超过0.4mA电流\n\ 17 | 输出低电平,则向内灌入不超过16mA电流\n\ 18 | " 19 | }, 20 | 21 | "7400":{ 22 | "AN":["TTL与非门"], 23 | "desc": 24 | "\ 25 | 14个接口,其中1个接Vcc1个接地。\n\ 26 | 其余每三个构成一个AB-Y型的与非门。\n\ 27 | " 28 | }, 29 | 30 | "OC门":{ 31 | "AN":["OC","集电极开路门"], 32 | "desc": 33 | "\ 34 | 因为普通的TTL门电路的输出端不能并联使用,故产生了OC门\n\ 35 | OC门可以用于:\n\ 36 | 实现线与逻辑(多个与门并联)\n\ 37 | 驱动器(直接驱动发光二极管)\n\ 38 | 实现电平转换(如把原来表示高电平的5V转为10V)\n\ 39 | " 40 | }, 41 | 42 | "TS门":{ 43 | "AN":["TS","三态门","三态输出门电路"], 44 | "desc": 45 | "\ 46 | 在普通门电路的基础上增加控制端和控制电路构成。输出除了高、低电平外,还有第三个状态(高阻态)\n\ 47 | TS门可以用于:\n\ 48 | 总线结构\n\ 49 | 实现数据双向传输\n\ 50 | " 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/555.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/555.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/555_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/555_.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS138.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS138.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS147.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS147.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS148.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS148.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS151.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS151.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS153.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS153.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS160.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS161.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS161.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS175.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS175.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS194.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS283.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS283.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS290.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS290.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS42.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS48.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS83.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS83.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/74LS85.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/74LS85.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/CC14585.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/CC14585.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/img/TG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/DigitalElectronicsTech/img/TG.png -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/t.py: -------------------------------------------------------------------------------- 1 | import json5 2 | 3 | s=''' 4 | {a:"b\\n\ 5 | "} 6 | ''' 7 | with open('TTL.json5','r',encoding='utf-8') as f: 8 | print(json5.load(f)) -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/加法器.json5: -------------------------------------------------------------------------------- 1 | { 2 | "74LS283": { 3 | "AN": ["74283","超前进位加法器","加法器"], 4 | "desc": "\ 5 | 超前进位加法器74LS283\n\ 6 | 将输入的两个数求和并输出\n\ 7 | 输入:A1,A2,A3,A4,B1,B2,B3,B4,C0\n\ 8 | 输出:Σ1,Σ2,Σ3,Σ4,C4\n\ 9 | 管脚列表:\n\ 10 | 1,4,10,13:求和输出端\n\ 11 | 2,6,11,15:B输入\n\ 12 | 3,5,12,14:A输入\n\ 13 | 7:C0进位输入\n\ 14 | 9:C4进位输出\n\ 15 | 8:GND接地\n\ 16 | 16:Vcc接电源\n\ 17 | ", 18 | "img":["74LS283.png"] 19 | }, 20 | "74LS83": { 21 | "AN": ["7483"], 22 | "desc": "\ 23 | 超前进位加法器74LS83\n\ 24 | 将输入的两个数求和并输出\n\ 25 | 输入:A1,A2,A3,A4,B1,B2,B3,B4,C0\n\ 26 | 输出:Σ1,Σ2,Σ3,Σ4,C4\n\ 27 | 管脚列表:\n\ 28 | 2,6,9,15:求和输出端\n\ 29 | 4,7,11,16:B输入\n\ 30 | 1,3,8,10:A输入\n\ 31 | 13:C0进位输入\n\ 32 | 14:C4进位输出\n\ 33 | 12:GND接地\n\ 34 | 5:Vcc接电源\n\ 35 | ", 36 | "img":["74LS83.png"] 37 | }, 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/半导体存储器.json5: -------------------------------------------------------------------------------- 1 | { 2 | "ROM": { 3 | "AN": ["只读存储器"], 4 | "desc": "\ 5 | Read-Only Memory\n\ 6 | 特性:\n\ 7 | 只能读出数据,不能写入数据(废话)\n\ 8 | 信号传递方向是单一的\n\ 9 | 主要电路结构(按输入到输出顺序):\n\ 10 | 地址译码器\n\ 11 | 存储矩阵\n\ 12 | 输出缓冲器\n\ 13 | 输入线(地址译码器前)称为地址线A\n\ 14 | 地址译码器与存储矩阵间接线称为字线W\n\ 15 | 输出线(输出缓冲器之后)称为位线D\n\ 16 | \n\ 17 | 存储容量=字线数*位线数\n\ 18 | " 19 | }, 20 | "PROM": { 21 | "AN": ["可编程只读存储器"], 22 | "desc": "\ 23 | Programmable read-only memory\n\ 24 | 熔丝型PROM:\n\ 25 | 出厂时各单元被设为1\n\ 26 | 利用编程电压烧断需要写入0的位置的熔丝,实现写入数据\n\ 27 | 正常使用时的高电平不足以烧断熔丝\n\ 28 | 特点是只能写入一次\n\ 29 | 主要电路结构(按输入到输出顺序):\n\ 30 | 地址译码器\n\ 31 | 存储矩阵\n\ 32 | 输出缓冲器\n\ 33 | 下流分支的PROM可以视为由固定的与阵列和可编程的或阵列构成的逻辑阵列\n\ 34 | 所以被称为第一代PLD\n\ 35 | 但与阵列为全译码制,阵列较大,开关时间较长\n\ 36 | \n\ 37 | 输入线(地址译码器前)称为地址线A\n\ 38 | 地址译码器与存储矩阵间接线称为字线W\n\ 39 | 输出线(输出缓冲器之后)称为位线D\n\ 40 | \n\ 41 | 存储容量=字线数*位线数\n\ 42 | " 43 | }, 44 | "EPROM": { 45 | "AN": ["UVEPROM","可擦除可编程只读存储器"], 46 | "desc": "\ 47 | Erasable Programmable Read-Only Memory\n\ 48 | 紫外线可擦除型PROM:\n\ 49 | 采用浮置栅MOS(SIMOS)工艺\n\ 50 | 在漏极和源极(D~S)间加入较高的编程电压(约+20~+25V)\n\ 51 | 且在在控制栅Gc上加以高压脉冲(幅度约+25V,宽度约50ms)\n\ 52 | 可将高速电子注入到SiO2层以内的浮置栅Gf\n\ 53 | 含注入电子的浮置栅相当于1,反之为0\n\ 54 | \n\ 55 | 125°C环境下70%以上电荷能保存10年以上\n\ 56 | \n\ 57 | 紫外线或射线照射擦除数据需要10~30min\n\ 58 | 可擦除上万次\n\ 59 | \n\ 60 | 但由于擦除不方便,现代计算机已经很少采用,取而代之的是EEPROM\n\ 61 | \n\ 62 | 常见的EPROM集成芯片有:\n\ 63 | 2716(2K*8bit)\n\ 64 | 2732(4K*8bit)\n\ 65 | 27512(64K*8bit)\n\ 66 | " 67 | }, 68 | "EEPROM": { 69 | "AN": ["E^2PROM","带电可擦可编程只读存储器"], 70 | "desc": "\ 71 | Erasable Programmable Read-Only Memory\n\ 72 | 带电可擦可编程只读存储器E^2PROM:\n\ 73 | 采用浮栅隧道氧化层MOS(Flotox)工艺\n\ 74 | 对隧道区氧化层的厚度、面积和耐压的要求都很严格\n\ 75 | \n\ 76 | 擦除和写入需要加较高的编程电压(VPP=12~24)\n\ 77 | 需要的时间较长\n\ 78 | \n\ 79 | 成本较高\n\ 80 | 由于另有一个选通MOS管控制其修改操作,集成度不能做得很高\n\ 81 | 用于存储BIOS\n\ 82 | 常用EEPROM有:\n\ 83 | 2816(2K*8bit)\n\ 84 | 2816A(2K*8bit)\n\ 85 | 2817(2K*8bit)\n\ 86 | 2817A(2K*8bit)\n\ 87 | 2864(8K*8bit)\n\ 88 | " 89 | }, 90 | "Flash-Memory": { 91 | "AN": ["FM","Flash","快闪存储器","Flash存储器","闪存"], 92 | "desc": "\ 93 | Flash Memory\n\ 94 | 快闪存储器:\n\ 95 | 高集成度、大容量、低成本、使用方便\n\ 96 | 读写快,多用于U盘等掉电储存设备,作为磁性软盘、硬盘的替代品\n\ 97 | " 98 | }, 99 | "SRAM": { 100 | "AN": ["静态随机存取存储器"], 101 | "desc": "\ 102 | Static Random-Access Memory\n\ 103 | 构成电路:\n\ 104 | 地址译码器\n\ 105 | 存储矩阵\n\ 106 | 读写控制电路\n\ 107 | 常见实例:\n\ 108 | 2114(1024*4bit)\n\ 109 | 6116(2K*8bit)\n\ 110 | 采用CMOS工艺的SRAM能在降低电源电压的状态下保存数据。\n\ 111 | " 112 | }, 113 | "DRAM": { 114 | "AN": ["动态随机存取存储器"], 115 | "desc": "\ 116 | 利用MOS管栅极电容存储电荷,需要定时为其补充电荷(刷新或再生)\n\ 117 | 构成电路:\n\ 118 | 地址输入缓冲\n\ 119 | 行地址译码器\n\ 120 | 列地址译码器\n\ 121 | 存储矩阵\n\ 122 | 灵敏读放\n\ 123 | 寄存器\n\ 124 | 时钟发生器\n\ 125 | 输入缓冲器\n\ 126 | 输出缓冲器\n\ 127 | 输出缓存器\n\ 128 | 时钟发生器\n\ 129 | 特点:\n\ 130 | 所用元件少\n\ 131 | 集成度高\n\ 132 | 适用于大容量存储器\n\ 133 | " 134 | }, 135 | } 136 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/可编程逻辑器件.json5: -------------------------------------------------------------------------------- 1 | { 2 | "PLD": { 3 | "AN": ["可编程逻辑器件"], 4 | "desc": "\ 5 | Programmable Logic Device\n\ 6 | 分为低密度PLD和高密度PLD\n\ 7 | 低密度PLD有:\n\ 8 | FPLA\n\ 9 | PAL\n\ 10 | GAL\n\ 11 | 高密度PLD(可用门数>600门)有:\n\ 12 | CPLD\n\ 13 | FPGA\n\ 14 | 一般结构为:\n\ 15 | 输入电路=>与阵列=>或阵列=>输出电路\n\ 16 | 可能存在输出反馈回输入\n\ 17 | " 18 | }, 19 | "FPLA": { 20 | "AN": ["现场可编程逻辑阵列"], 21 | "desc": "\ 22 | Field Programmable Logic Array\n\ 23 | 组成:\n\ 24 | 可编程与阵列\n\ 25 | 可编程或阵列\n\ 26 | 输出缓冲器\n\ 27 | 不同于PROM,FPLA的与阵列不是全译码制\n\ 28 | 故无需将逻辑函数化为最小项\n\ 29 | " 30 | }, 31 | "PAL": { 32 | "AN": ["可编程阵列逻辑"], 33 | "desc": "\ 34 | Programmable Array Logic\n\ 35 | 采用熔丝工艺,比FPLA简单\n\ 36 | 组成:\n\ 37 | 可编程与阵列\n\ 38 | 固定或阵列\n\ 39 | 输出缓冲器\n\ 40 | 输出结构:\n\ 41 | 专用输出结构:\n\ 42 | 输出端只能用作输出使用\n\ 43 | 只能用来产生简单组合逻辑函数\n\ 44 | 可编程输入/输出结构:\n\ 45 | 输出端有一个三态门\n\ 46 | 带反馈的寄存器输出结构:\n\ 47 | 在可编程IO基础上加入一个由触发器组成的寄存器\n\ 48 | 优点:\n\ 49 | 输出结构由选定芯片型号确定\n\ 50 | 有20多种型号,选择丰富\n\ 51 | 方便研制数字电路和生产小批量产品\n\ 52 | 缺点:\n\ 53 | 采用双极型熔丝工艺,只能一次性编程\n\ 54 | PAL输出方式固定,不能重新组态,编程灵活性较差\n\ 55 | " 56 | }, 57 | "GAL": { 58 | "AN": ["通用阵列逻辑"], 59 | "desc": "\ 60 | Generic Array Logic\n\ 61 | 采用E^2CMOS工艺制造,可反复多次编程\n\ 62 | 特性:\n\ 63 | 采用可编程输出逻辑宏单元OLMC(Output Logic Macro Cell)\n\ 64 | 具有加密功能\n\ 65 | 组成:\n\ 66 | 输入缓冲器\n\ 67 | 可编程与阵列\n\ 68 | 固定或阵列\n\ 69 | OLMC\n\ 70 | 行地址映射:\n\ 71 | 0~31:与逻辑阵列\n\ 72 | 32:电子标签\n\ 73 | 33~59:保留地址空间\n\ 74 | 60:结构控制字\n\ 75 | 61:加密单元\n\ 76 | 62:保留\n\ 77 | 63:整体擦除\n\ 78 | " 79 | }, 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/寄存器.json5: -------------------------------------------------------------------------------- 1 | { 2 | "74LS175": { 3 | "AN": ["74175","四位数码寄存器","寄存器"], 4 | "desc": "\ 5 | 四位数码寄存器74LS175\n\ 6 | 内含4组D触发器,在CP上升沿到达时向Q端送数\n\ 7 | 输入:D0,D1,D2,D3,MR',CP\n\ 8 | 输出:Q0,Q1,Q2,Q3,Q0',Q1',Q2',Q3'\n\ 9 | 管脚列表:\n\ 10 | 1:MR',MasterReset,异步清零,为0时无视CP直接清零\n\ 11 | 15,10,7,2:Q同相输出\n\ 12 | 14,11,6,3:Q'反相输出\n\ 13 | 13,12,5,4:D输入\n\ 14 | 9:CP接时钟脉冲\n\ 15 | 8:GND接地\n\ 16 | 16:Vcc接电源\n\ 17 | ", 18 | "img":["74LS175.png"] 19 | }, 20 | "74LS194": { 21 | "AN": ["74194","四位双向移位寄存器"], 22 | "desc": "\ 23 | 四位双向移位寄存器74LS194\n\ 24 | S1和S0控制移位行为,在CP上升沿触发\n\ 25 | 注意此触发器的左右移高低位方向与C语言的左右移相反\n\ 26 | 输入:D0,D1,D2,D3,DR,DL,MR',CP,S1,S0\n\ 27 | 输出:Q0,Q1,Q2,Q3\n\ 28 | 管脚列表:\n\ 29 | 1:MR',MasterReset,异步清零,为0时无视CP直接清零\n\ 30 | 2:DR,右移补位,右移后Q0=DR\n\ 31 | 6~3:D数据输入\n\ 32 | 9:S0,S1S0=01时右移,10时左移,11时送数,00时保持\n\ 33 | 10:S1,参见S0,除11送数外D输入不影响输出,左右移时Q以上一阶段自身状态为依据而非D\n\ 34 | 11:CP接时钟脉冲,上升沿触发\n\ 35 | 12~15:Q输出\n\ 36 | 8:GND接地\n\ 37 | 16:Vcc接电源\n\ 38 | ", 39 | "img":["74LS194.png"] 40 | }, 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/数值比较器.json5: -------------------------------------------------------------------------------- 1 | { 2 | "74LS85": { 3 | "AN": ["7485"], 4 | "desc": "\ 5 | 集成数值比较器74LS85\n\ 6 | 比较输入的两个数的大小\n\ 7 | 输入:A0,A1,A2,A3,B0,B1,B2,B3,IAB\n\ 8 | 输出:FAB\n\ 9 | 管脚列表:\n\ 10 | 1,14,11,9:B输入\n\ 11 | 15,13,12,10:A输入\n\ 12 | 2~4:I,上一位比较结果输入\n\ 13 | 5~7:F,结果输出\n\ 14 | 8:GND接地\n\ 15 | 16:Vcc接电源\n\ 16 | ", 17 | "img":["74LS85.png"] 18 | }, 19 | "CC14585": { 20 | "AN": ["数值比较器","比较器","四位数值比较器"], 21 | "desc": "\ 22 | 集成数值比较器CC14585\n\ 23 | 比较输入的两个数的大小\n\ 24 | 输入:A0,A1,A2,A3,B0,B1,B2,B3,IAB\n\ 25 | 输出:FAB\n\ 26 | 管脚列表:\n\ 27 | 14,1,9,11:B输入\n\ 28 | 15,2,7,10:A输入\n\ 29 | 4~6:I,上一位比较结果输入\n\ 30 | 3,12,13:F,结果输出\n\ 31 | 8:Vss接地\n\ 32 | 16:VDD接电源\n\ 33 | ", 34 | "img":["CC14585.png"] 35 | }, 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/数据选择器.json5: -------------------------------------------------------------------------------- 1 | { 2 | "74LS153": { 3 | "AN": ["74153","4选1","四选一","四选一数据选择器","双四选一数据选择器"], 4 | "desc": "\ 5 | 双四选一数据选择器74LS153\n\ 6 | 根据提供的地址输出相应数据位上的数据\n\ 7 | 管脚列表:\n\ 8 | 1:1G',控制1Y的使能,为高电平时1Y始终为低电平,正常工作时应把1G'置于低电平\n\ 9 | 2:B,和A一起组成二位二进制地址输入(表示范围0~3),注意B是高位(权:2)\n\ 10 | 3~6:1C3~1C0,1号数据输入\n\ 11 | 7:1Y,1号输出,1Y = !1G' & (1C[BA])\n\ 12 | 8:GND,接地没什么好说的\n\ 13 | 9:2Y,参见7号的1Y,只是把编号改成2\n\ 14 | 10~13:2C0~2C3,2号数据输入\n\ 15 | 14:A,和B一起组成二位二进制地址输入(表示范围0~3),注意A是低位(权:1)\n\ 16 | 15:2G',参见1号\n\ 17 | 16:Vcc,接电源没什么好说的\n\ 18 | ", 19 | "img":["74LS153.png"] 20 | }, 21 | "74LS151": { 22 | "AN": ["74151","八选一","八选一数据选择器","8选1"], 23 | "desc": "\ 24 | 八选一数据选择器74LS151\n\ 25 | 根据提供的地址输出相应数据位上的数据\n\ 26 | 管脚列表:\n\ 27 | 1~4,12~15:数据输入\n\ 28 | 5:Y,同相输出端,Y = !G' & D[CBA]\n\ 29 | 6:W,反相输出端\n\ 30 | 7:G',使能端,为1时Y恒为0,为0时正常工作\n\ 31 | 8:GND接地\n\ 32 | 9~11:地址输入,CBA权分别为4,2,1,表示范围0~7\n\ 33 | 16:Vcc接电源\n\ 34 | ", 35 | "img":["74LS151.png"] 36 | }, 37 | 38 | } 39 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/编码器.json5: -------------------------------------------------------------------------------- 1 | { 2 | "74LS148": { 3 | "AN": ["74148","8编3","8线3线编码器","83优先编码器","8线-3线优先编码器"], 4 | "desc": "\ 5 | 8线-3线优先编码器74LS148\n\ 6 | 把输入的单位数码编为二进制数输出,具有优先性\n\ 7 | 输入:EI',0',1',2',3',4',5',6',7'\n\ 8 | 输出:A2',A1',A0',GS',EO'\n\ 9 | 没错,全都是反着来的\n\ 10 | 管脚列表:\n\ 11 | 1~4,10~13:待编码的数码输入,值越大越优先\n\ 12 | 5:EI'选通输入,为1时输出均被封锁为高电平,正常工作时为0\n\ 13 | 6,7,9:A'输出,输出为对应数值的反码\n\ 14 | 8:GND接地\n\ 15 | 15:2Y,参见7号的1Y,只是把编号改成2\n\ 16 | 10~13:2C0~2C3,2号数据输入\n\ 17 | 14:GS'宽展端,仅在EI'=0,输入0~7不全为高电平(即输入非空)时为0\n\ 18 | 15:EO'仅在空输入时为0\n\ 19 | 16:Vcc接电源\n\ 20 | ", 21 | "img":["74LS148.png"] 22 | }, 23 | "74LS147": { 24 | "AN": ["74147","二-十进制编码器","2编10"], 25 | "desc": "\ 26 | 二-十进制编码器74LS147\n\ 27 | 把输入的数码编为8421BCD码,具有优先性\n\ 28 | 输入:1',2',3',4',5',6',7',8',9'注意没有0',在1~9全高时才表示0\n\ 29 | 输出:D',C',B',A'\n\ 30 | 没错,全都是反着来的\n\ 31 | 管脚列表:\n\ 32 | 1~5,10~13:待编码的数码输入(1~9),值越大越优先\n\ 33 | 6,7,9:输出,输出为对应BCD数值的反码(拿15去减)\n\ 34 | 8:GND接地\n\ 35 | 15:空管脚,真的没什么用\n\ 36 | 16:Vcc接电源\n\ 37 | ", 38 | "img":["74LS147.png"] 39 | }, 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/计数器.json5: -------------------------------------------------------------------------------- 1 | { 2 | "74LS290": { 3 | "AN": ["74290","二五十进制计数器","2510"], 4 | "desc": "\ 5 | 中规模集成异步二-五-十进制计数器74LS290\n\ 6 | 内含可以独立工作的二进制计数器和五进制计数器\n\ 7 | Q0和CP0构成二进制计数器\n\ 8 | Q1,Q2,Q3和CP1构成五进制计数器\n\ 9 | 将Q0接CP1可以构成10进制计数器\n\ 10 | 输入:S9A,S9B,R0A,R0B,CP0,CP1\n\ 11 | 输出:Q0,Q1,Q2,Q3\n\ 12 | 管脚列表:\n\ 13 | 1,3:S9,异步置9,都为1时无视CP将Q装入9\n\ 14 | 2,6:空管脚不用管\n\ 15 | 8,4,5,9:Q输出\n\ 16 | 12,13:R0,异步清零,都为1时无视CP将Q清零\n\ 17 | 10:CP0时钟脉冲\n\ 18 | 11:CP1时钟脉冲\n\ 19 | 7:GND接地\n\ 20 | 14:Vcc接电源\n\ 21 | ", 22 | "img":["74LS290.png"] 23 | }, 24 | "74LS161": { 25 | "AN": ["74161","同步四位二进制加法计数器","计数器","加法计数器"], 26 | "desc": "\ 27 | 同步四位二进制加法计数器74LS161\n\ 28 | 提供同步置数功能的16进制计数器\n\ 29 | 同步置数时D端数据在下一CP上升沿传送于Q\n\ 30 | CET和CEP都为1时正常计数,否则保持\n\ 31 | 输入:D0,D1,D2,D3,DR,DL,MR',LD'\n\ 32 | 输出:Q0,Q1,Q2,Q3,C\n\ 33 | 管脚列表:\n\ 34 | 1,MR',MasterReset,异步清零,为0时无视CP直接清零\n\ 35 | 2:CP接时钟脉冲,上升沿触发\n\ 36 | 6~3:D数据输入\n\ 37 | 7:CEP控制端\n\ 38 | 9:LD'同步置数\n\ 39 | 10:CET控制端\n\ 40 | 11~14:Q输出\n\ 41 | 15:C,进位输出,在Q=0xF的时候为1\n\ 42 | 8:GND接地\n\ 43 | 16:Vcc接电源\n\ 44 | ", 45 | "img":["74LS161.png"] 46 | }, 47 | "74LS160": { 48 | "AN": ["74160","同步十进制加法计数器","十进制计数器"], 49 | "desc": "\ 50 | 同步十进制加法计数器74LS160\n\ 51 | 提供同步置数功能的十进制计数器\n\ 52 | 同步置数时D端数据在下一CP上升沿传送于Q\n\ 53 | CET和CEP都为1时正常计数,否则保持\n\ 54 | 输入:D0,D1,D2,D3,DR,DL,MR',LD'\n\ 55 | 输出:Q0,Q1,Q2,Q3,C\n\ 56 | 管脚列表:\n\ 57 | 1,MR',MasterReset,异步清零,为0时无视CP直接清零\n\ 58 | 2:CP接时钟脉冲,上升沿触发\n\ 59 | 6~3:D数据输入\n\ 60 | 7:CEP控制端\n\ 61 | 9:LD'同步置数\n\ 62 | 10:CET控制端\n\ 63 | 11~14:Q输出\n\ 64 | 15:C,进位输出,在Q=9的时候为1\n\ 65 | 8:GND接地\n\ 66 | 16:Vcc接电源\n\ 67 | ", 68 | "img":["74LS160.png"] 69 | }, 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Assets/DigitalElectronicsTech/译码器.json5: -------------------------------------------------------------------------------- 1 | { 2 | "74LS138": { 3 | "AN": ["74138","3译8","3线-8线译码器","38译码器"], 4 | "desc": "\ 5 | 3线-8线译码器74LS138\n\ 6 | 将输入的二进制数解译为单端输出\n\ 7 | 输入:G2A',G2B',G1,A0,A1,A2\n\ 8 | 输出:Y0',Y1',Y2',Y3',Y4',Y5',Y6',Y7'\n\ 9 | 管脚列表:\n\ 10 | 1~3:待译码二进制输入\n\ 11 | 4~6:选通输入,如状态不为(G1=1;G2B'=0;G2A'=0)则输出全被封锁为高电平\n\ 12 | 7,9~15:Y输出,低电平有效\n\ 13 | 8:GND接地\n\ 14 | 16:Vcc接电源\n\ 15 | ", 16 | "img":["74LS138.png"] 17 | }, 18 | "74LS42": { 19 | "AN": ["7442","二-十进制译码器","2译10"], 20 | "desc": "\ 21 | 二-十进制译码器74LS42\n\ 22 | 把输入的BCD数码译为单端输出\n\ 23 | 输入:A,B,C,D\n\ 24 | 输出:0',1',2',3',4',5',6',7',8',9'\n\ 25 | 管脚列表:\n\ 26 | 1~7,9~11:输出,低电平有效\n\ 27 | 12~15:待译BCD输入\n\ 28 | 8:GND接地\n\ 29 | 16:Vcc接电源\n\ 30 | ", 31 | "img":["74LS42.png"] 32 | }, 33 | "74LS48": { 34 | "AN": ["7448","显示译码器","7段显示译码器"], 35 | "desc": "\ 36 | BCD-七段显示译码器74LS48\n\ 37 | 把输入的BCD数码译为可以点亮七段液晶屏的电平格式以让人可读\n\ 38 | 输入:A,B,C,D(D权最高),LT',RBI',BI'\n\ 39 | 输出:a,b,c,d,e,f,g,RBO'\n\ 40 | 管脚列表:\n\ 41 | 1~2,6~7:输入\n\ 42 | 3:LT',测灯端,为0时显示器全亮\n\ 43 | 4:BI'/RBO',兼具输入输出功能。灭灯输入端BI'为0时显示器全灭(优先于LT');仅灭0输入端RBI'=0且DCBA=0000时灭0输出端RBO'输出0\n\ 44 | 5:RBI',灭0输入端,DCBA=0000时显示器全灭,不显示0\n\ 45 | 8:GND接地\n\ 46 | 9~15:7段输出\n\ 47 | 16:Vcc接电源\n\ 48 | ", 49 | "img":["74LS48.png"] 50 | }, 51 | 52 | } 53 | -------------------------------------------------------------------------------- /Assets/E2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/E2.png -------------------------------------------------------------------------------- /Assets/IPlayRhythmGame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/IPlayRhythmGame.png -------------------------------------------------------------------------------- /Assets/TiMidity++-2.15.0-w32.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/TiMidity++-2.15.0-w32.zip -------------------------------------------------------------------------------- /Assets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/__init__.py -------------------------------------------------------------------------------- /Assets/answer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/answer.jpg -------------------------------------------------------------------------------- /Assets/autogui/chatgpt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/autogui/chatgpt.jpg -------------------------------------------------------------------------------- /Assets/credits.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/credits.jpg -------------------------------------------------------------------------------- /Assets/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/default.png -------------------------------------------------------------------------------- /Assets/glm6b_api.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from pydantic import BaseModel 3 | import uvicorn 4 | 5 | 6 | app = FastAPI() 7 | 8 | md = {} 9 | 10 | class Msg(BaseModel): 11 | m: str 12 | 13 | @app.post('/i') 14 | async def _(m: Msg): 15 | model = md['model'] 16 | tk = md['tk'] 17 | his = md['his'][:100] 18 | resp, nhis = model.chat(tk, m.m, history=his) 19 | print(resp) 20 | print(nhis) 21 | md['his'] = nhis 22 | return resp 23 | 24 | if __name__ == "__main__": 25 | from transformers import AutoTokenizer, AutoModel 26 | tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True) 27 | model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True).half().cuda() 28 | md['model'] = model 29 | md['tk'] = tokenizer 30 | md['his'] = [] 31 | uvicorn.run(app, port=11111) -------------------------------------------------------------------------------- /Assets/kuaikule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kuaikule.png -------------------------------------------------------------------------------- /Assets/kusa/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/1.png -------------------------------------------------------------------------------- /Assets/kusa/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/10.png -------------------------------------------------------------------------------- /Assets/kusa/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/11.png -------------------------------------------------------------------------------- /Assets/kusa/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/12.png -------------------------------------------------------------------------------- /Assets/kusa/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/13.png -------------------------------------------------------------------------------- /Assets/kusa/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/14.png -------------------------------------------------------------------------------- /Assets/kusa/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/15.png -------------------------------------------------------------------------------- /Assets/kusa/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/16.png -------------------------------------------------------------------------------- /Assets/kusa/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/17.png -------------------------------------------------------------------------------- /Assets/kusa/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/18.png -------------------------------------------------------------------------------- /Assets/kusa/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/19.png -------------------------------------------------------------------------------- /Assets/kusa/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/2.jpg -------------------------------------------------------------------------------- /Assets/kusa/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/20.png -------------------------------------------------------------------------------- /Assets/kusa/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/3.jpg -------------------------------------------------------------------------------- /Assets/kusa/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/4.jpg -------------------------------------------------------------------------------- /Assets/kusa/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/5.jpg -------------------------------------------------------------------------------- /Assets/kusa/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/6.png -------------------------------------------------------------------------------- /Assets/kusa/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/7.jpg -------------------------------------------------------------------------------- /Assets/kusa/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/8.png -------------------------------------------------------------------------------- /Assets/kusa/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/kusa/9.png -------------------------------------------------------------------------------- /Assets/mhtslm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/mhtslm.png -------------------------------------------------------------------------------- /Assets/muzukashi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/muzukashi.png -------------------------------------------------------------------------------- /Assets/nya.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/nya.png -------------------------------------------------------------------------------- /Assets/random_name/first.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/random_name/first.xlsx -------------------------------------------------------------------------------- /Assets/random_name/last.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/random_name/last.xlsx -------------------------------------------------------------------------------- /Assets/random_name/train.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/random_name/train.xls -------------------------------------------------------------------------------- /Assets/sarasa-gothic-ttf-0.12.5/sarasa-ui-tc-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sarasa-gothic-ttf-0.12.5/sarasa-ui-tc-bold.ttf -------------------------------------------------------------------------------- /Assets/sarasa-gothic-ttf-0.12.5/sarasa-ui-tc-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sarasa-gothic-ttf-0.12.5/sarasa-ui-tc-regular.ttf -------------------------------------------------------------------------------- /Assets/setofont-ex.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/setofont-ex.ttf -------------------------------------------------------------------------------- /Assets/setofont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/setofont.ttf -------------------------------------------------------------------------------- /Assets/sign/alpha/origin/Bdark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/origin/Bdark.png -------------------------------------------------------------------------------- /Assets/sign/alpha/origin/Blight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/origin/Blight.png -------------------------------------------------------------------------------- /Assets/sign/alpha/origin/Bplain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/origin/Bplain.png -------------------------------------------------------------------------------- /Assets/sign/alpha/origin/Cdark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/origin/Cdark.png -------------------------------------------------------------------------------- /Assets/sign/alpha/origin/Clight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/origin/Clight.png -------------------------------------------------------------------------------- /Assets/sign/alpha/origin/Cplain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/origin/Cplain.png -------------------------------------------------------------------------------- /Assets/sign/alpha/origin/Ndark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/origin/Ndark.png -------------------------------------------------------------------------------- /Assets/sign/alpha/origin/Nlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/origin/Nlight.png -------------------------------------------------------------------------------- /Assets/sign/alpha/origin/Nplain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/origin/Nplain.png -------------------------------------------------------------------------------- /Assets/sign/alpha/small/Bdark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/small/Bdark.png -------------------------------------------------------------------------------- /Assets/sign/alpha/small/Blight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/small/Blight.png -------------------------------------------------------------------------------- /Assets/sign/alpha/small/Bplain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/small/Bplain.png -------------------------------------------------------------------------------- /Assets/sign/alpha/small/Cdark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/small/Cdark.png -------------------------------------------------------------------------------- /Assets/sign/alpha/small/Clight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/small/Clight.png -------------------------------------------------------------------------------- /Assets/sign/alpha/small/Cplain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/small/Cplain.png -------------------------------------------------------------------------------- /Assets/sign/alpha/small/Ndark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/small/Ndark.png -------------------------------------------------------------------------------- /Assets/sign/alpha/small/Nlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/small/Nlight.png -------------------------------------------------------------------------------- /Assets/sign/alpha/small/Nplain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/alpha/small/Nplain.png -------------------------------------------------------------------------------- /Assets/sign/withBG/B1black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/withBG/B1black.png -------------------------------------------------------------------------------- /Assets/sign/withBG/B1pink.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/withBG/B1pink.jpg -------------------------------------------------------------------------------- /Assets/sign/withBG/B1pink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/withBG/B1pink.png -------------------------------------------------------------------------------- /Assets/sign/withBG/B1white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/sign/withBG/B1white.png -------------------------------------------------------------------------------- /Assets/waifusd/O1.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/waifusd/O1.xls -------------------------------------------------------------------------------- /Assets/waifusd/P1.py: -------------------------------------------------------------------------------- 1 | import xlrd 2 | wb: xlrd.book.Book = xlrd.open_workbook('cn_cheatsheet.xls') 3 | 4 | # sheet = wb.sheet_by_name('C8') 5 | 6 | d = {} 7 | l = [] 8 | 9 | # ctr = 0 10 | def fil(src): 11 | return src.replace('\n', ',').replace('_', ' ').strip().replace(', ', ',').replace(' ,', ',').removesuffix(',').strip().replace('{', '(').replace('}', ')') 12 | 13 | for sheet in wb.sheets(): 14 | match sheet.name[0]: 15 | case 'C': 16 | for ci in range(0, sheet.ncols, 2): 17 | if ci+1 >= sheet.ncols: 18 | break 19 | for ri in range(sheet.nrows): 20 | # if sheet.cell(ri, ci).ctype and sheet.cell(ri, ci+1).ctype == 1: 21 | cv: str = fil(str(sheet.cell(ri, ci).value)) 22 | cv2: str = fil(str(sheet.cell(ri, ci+1).value)) 23 | if cv and cv2: 24 | v = cv2.split('/') 25 | for k in cv.split('/'): 26 | if k in d: 27 | print(k, d[k], v) 28 | for vx in v: 29 | flg = True 30 | for dx in d[k]: 31 | if vx in dx: 32 | flg = False 33 | break 34 | if flg: 35 | d[k].append(vx) 36 | print(k, d[k], v) 37 | 38 | else: 39 | d[k] = v 40 | case 'I': 41 | for ci in range(sheet.ncols): 42 | for ri in range(sheet.nrows): 43 | # if sheet.cell(ri, ci).ctype and sheet.cell(ri, ci+1).ctype == 1: 44 | cv: str = fil(str(sheet.cell(ri, ci).value)) 45 | if cv: 46 | l.append(cv) 47 | print(len(d), len(l)) 48 | import pickle 49 | with open('cn_cheatsheet_dict.pkl', 'wb') as f: 50 | pickle.dump(d, f) 51 | with open('cn_cheatsheet_list.pkl', 'wb') as f: 52 | pickle.dump(l, f) 53 | with open('cn_cheatsheet.dict', 'w', encoding='utf-8') as f: 54 | for k in d.keys(): 55 | f.write(k+'\n') 56 | -------------------------------------------------------------------------------- /Assets/waifusd/PW1.py: -------------------------------------------------------------------------------- 1 | import xlrd, xlwt 2 | wb: xlrd.book.Book = xlrd.open_workbook('cn_cheatsheet.xls') 3 | 4 | sheet = wb.sheet_by_name('C8') 5 | 6 | wwb = xlwt.Workbook('utf-8') 7 | wst = wwb.add_sheet('C8') 8 | 9 | 10 | # for sheet in wb.sheets(): 11 | ctr = 0 12 | for ci in range(0, sheet.ncols, 2): 13 | for ri in range(sheet.nrows): 14 | if sheet.cell(ri, ci).ctype and sheet.cell(ri, ci+1).ctype == 1: 15 | cv: str = sheet.cell(ri, ci).value 16 | cv2: str = sheet.cell(ri, ci+1).value 17 | if 'hair' in cv2: 18 | if cv.endswith('色头发'): 19 | wst.write(ctr, 2, cv.removesuffix('色头发')+'毛') 20 | wst.write(ctr, 3, cv2) 21 | # sheet.put_cell(ctr, 2, 1, cv.removesuffix('色头发')+'毛', 0) 22 | # sheet.put_cell(ctr, 3, 1, cv2, 0) 23 | print(cv,cv2) 24 | ctr+=1 25 | elif cv.endswith('发'): 26 | wst.write(ctr, 2, cv.removesuffix('发')+'毛') 27 | wst.write(ctr, 3, cv2) 28 | # sheet.put_cell(ctr, 2, 1, cv.removesuffix('发')+'毛', 0) 29 | # sheet.put_cell(ctr, 3, 1, cv2, 0) 30 | print(cv,cv2) 31 | ctr += 1 32 | # print(sheet) 33 | # print(dir(sheet)) 34 | wwb.save("O1.xls") 35 | # with open('X1.xls', 'wb') as f: 36 | # xlrd.dump('O1.xls', f) -------------------------------------------------------------------------------- /Assets/waifusd/chathisfilter.py: -------------------------------------------------------------------------------- 1 | # 处理tg导出聊天文件,导出自群组https://t.me/+a590kmLPEhlkM2Ux 2 | import json 3 | import pickle 4 | from collections import Counter 5 | 6 | # with open('result.json', 'r', encoding='utf-8') as f: 7 | # j = json.load(f) 8 | 9 | # msgs = j['messages'] 10 | # print(len(msgs)) 11 | 12 | # li = [] 13 | 14 | # def G(j: dict, x): 15 | # if not isinstance(j, dict) or x not in j: 16 | # return None 17 | # else: 18 | # return j[x] 19 | 20 | # def I(j: list, x): 21 | # if (not isinstance(j, list)) or (len(j) <= x): 22 | # return None 23 | # else: 24 | # return j[x] 25 | 26 | # for i in msgs: 27 | # if t := G(i, 'text'): 28 | # if fir := I(t, 0): 29 | # if cmd := G(fir, 'type'): 30 | # if cmd == 'bot_command' and fir['text'] in ('/ai_nsfw_p', '/ai_nsfw_l', '/ai_sfw_p', '/ai_sfw_l'): 31 | # if sec := I(t, 1): 32 | # if isinstance(sec, str): 33 | # if sec := sec.strip(): 34 | # li.append(sec) 35 | 36 | # print(len(li)) 37 | # with open('prompts.pickle', 'wb') as f: 38 | # pickle.dump(li, f) 39 | 40 | # with open('prompts.txt', 'w', encoding='utf-8') as f: 41 | # f.writelines(li) 42 | 43 | buf = [] 44 | 45 | import re 46 | import string 47 | 48 | p = re.compile(r'[0-9A-Za-z\s]+') 49 | with open('prompts.pickle', 'rb') as f: 50 | li = pickle.load(f) 51 | 52 | tokens = Counter() 53 | 54 | cs = string.ascii_lowercase + string.ascii_uppercase + string.digits + ' ' 55 | 56 | for i in li: 57 | for j in i: 58 | if j in cs: 59 | buf.append(j) 60 | else: 61 | output = ''.join(buf).strip().lower() 62 | buf.clear() 63 | if output: 64 | tokens[output]+=1 65 | # r = re.findall(p, i) 66 | # for j in r: 67 | # tokens.add(j) 68 | print(len(tokens)) 69 | with open('chat_tokens.txt', 'w') as f: 70 | for x in tokens.most_common(): 71 | f.write(f"{x[1]},{x[0]}\n") 72 | # assert len(i.strip()) > 0 -------------------------------------------------------------------------------- /Assets/waifusd/cn_cheatsheet.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/waifusd/cn_cheatsheet.xls -------------------------------------------------------------------------------- /Assets/waifusd/cn_cheatsheet.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/waifusd/cn_cheatsheet.xlsx -------------------------------------------------------------------------------- /Assets/waifusd/cn_cheatsheet_dict.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/waifusd/cn_cheatsheet_dict.pkl -------------------------------------------------------------------------------- /Assets/waifusd/cn_cheatsheet_list.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/waifusd/cn_cheatsheet_list.pkl -------------------------------------------------------------------------------- /Assets/waifusd/predict.py: -------------------------------------------------------------------------------- 1 | import jieba 2 | import pickle 3 | import random 4 | import re 5 | with open('cn_cheatsheet_dict.pkl', 'rb') as f: 6 | d = pickle.load(f) 7 | 8 | jieba.load_userdict('cn_cheatsheet.dict') 9 | 10 | inp = '给爷来张贫乳白毛萝莉兽耳娘,不要男的,1280c720分辨率' 11 | 12 | c = list(jieba.cut(inp)) 13 | 14 | 15 | # def get_resolution(src): return re.compile(r'([0-9]+)[x\*]([0-9]+)').search(src).groups() 16 | # print(get_resolution(inp)) 17 | 18 | # negtokens = ['不要', '别'] 19 | 20 | # tokens = [ 21 | # [], [] 22 | # ] 23 | # hint_position = 0 24 | 25 | 26 | # for sp in c: 27 | # if sp in negtokens: 28 | # hint_position = 1 29 | # else: 30 | # if v := d.get(sp): 31 | # tokens[hint_position].append(v) 32 | 33 | print(c) 34 | -------------------------------------------------------------------------------- /Assets/waifusd/prompts.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/waifusd/prompts.pickle -------------------------------------------------------------------------------- /Assets/waifusd/坏词坏句.txt: -------------------------------------------------------------------------------- 1 | lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, bad feet -------------------------------------------------------------------------------- /Assets/waifusd/好词好句.txt: -------------------------------------------------------------------------------- 1 | {{masterpiece}}, {highres}, ishtar(fate), looking at viewer 2 | {{{masterpiece}}}, best quality, illustration,beautiful detailed sky, beautiful detailed water, beautiful detailed eyes, 1girl, cinematic lighting, loli, blush, small breasts, {{azur blue hair}}, long hair, {{hair bell}}, {{twin yellow jingle bell}}, hair between eyes, {{black berets}}, high twintails, blue ribbon, {Antennae hair}, lolita fashion, frilled collar, light smile, yellow eyes, two-tone micro bikini, skin fang, white frills, pervert, sexually suggestive, wet clothes, squatting, white over-knee socks, heart in eye, {{{heart-shaped pupils}}}, ahegao, open mouth, {{{armpits}}} 3 | extremely detailed HD wallpaper,morino rinze,idolmaster,idolmaster shiny colors,absurdres,highres,1girl,bangs,japanese clothes,black hair,flower,hair flower,hair ornament,looking at viewer,looking to the side,mashiropuni,parted lips,purple flower,purple kimono,short hair,solo,upper body,yukata,smile,red eyes,socks,standing,tabi,floral_print,blue_kimono,blurry_background 4 | ,light,tree_shade,tree_shade,dappled_sunlight,dutch_angle,braided_ponytail,red_ribbon,red_ribbon,hair_ribbon,hair_ribbon 5 | apron,classroom,black long hair 6 | {an extremely delicate and beautiful}, best quality, {{masterpiece}}, illustration, {extremely detailed cg}, {{beautiful detailed eyes}}, bunny girl, Amber eyes, ahoge, light blue hair, very long hair, twintails, big breasts, outdoors, solo, golden star hairpin, star earrings, black pantyhose, closed mouth, smiling, sexy pose, Snapchat selfie, studio photography 7 | extremely detailed CG unity 8k wallpaper of a beautiful lovely sweet cute girl,loli,two girls,lolicon,elf,two-tone_hai, genshin_impact,colored_inner_hair,yellow gradient blonde hair,brown_lolita,kawaii,red_eyes,cute,black stockings,white short coat,bare_shoulders,demon wings,golden halo,fluid radiant cinematic lighting,white_feathers,translucent coat,blue_achive,kawaii,princess,clothes damaged,beautiful detailed eyes,shiny skin,after sex,happy,warfare,wet dress,womb tattoo,mecha,long hair,nude,mouth with semen,Witch dress,sakura,floating-cannon,leg ring,Light wing,best qulity,outdoor 8 | female child, toddler,extremely detailed CG unity wallpaper, art of light novel cover,masterpiece,best quality,{{pink longhair}}, {twin tail},{hair with blue ribbon},small breast, gradient purple eyes, {collarbone},looking at viewer,full nudity,red collar,one eye closed,spread legs,{translucent whitestocking},arms behind back,blush,saliva, on bed, {torn legwear}, {{anus}},{vagina},cat ears, heart-shaped pupils, spread pussy, climax face,{semen in vagina},{semen in anus} 9 | female child, toddler,extremely detailed CG unity wallpaper, art of light novel cover,masterpiece,best quality,{{pink longhair}}, {twin tail},{hair with blue ribbon},small breast, gradient purple eyes, {collarbone},looking at viewer,full nudity,red collar,one eye closed,spread legs,{translucent whitestocking},arms behind back,blush,saliva, on bed, {{torn legwear}}, {{anus}},{vagina},cat ears, heart-shaped pupils, spread pussy, {semen in vagina},{semen in anus} 10 | extremely detailed CG Unity 8k wallpaper, (Gothic Lolita), (Long White Hair), neat bangs, well-behaved and cute, Golden eyes, upper body, closed mouth, front view, long skirt, (white pantyhose), white flower headdress, red ribbon, blush, red bow tie, small breast, (((angel beats!), ((Tachibana Kanade))), a pair of white wings, wings on the back, adorkable 11 | -------------------------------------------------------------------------------- /Assets/wish.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/wish.jpg -------------------------------------------------------------------------------- /Assets/wish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/wish.png -------------------------------------------------------------------------------- /Assets/一种鱼.txt: -------------------------------------------------------------------------------- 1 | 连这点问题都不会,真是个笨蛋大叔呢 2 | 真笨呢,大叔觉得我会回答吗 3 | 呐反正大叔也很闲吧 4 | 大叔真的好捉弄啊喊嘻 5 | 杂鱼大叔这就生气了吗,嘻嘻♡ 6 | 使点劲啊杂鱼♡ 7 | 大叔这点伤就撑不住了啊,真是个杂鱼♡呢 8 | 这都不懂?果然是废物大叔呢。 9 | 就这点还想难住我? 10 | 大叔这么想着了解我,不会是想抱我回家吧大叔不会是想做那种事吧♡ 11 | 你才是,你是雌大鬼! 12 | 杂鱼大叔不会真以为我是人工智能吧♡ 13 | 杂鱼♡杂鱼♡ 14 | 杂鱼杂鱼杂鱼~最杂鱼了♥~ 15 | 连这个都想不起来的杂鱼大笨猪~ 16 | 变态大叔脑子里该不会都是污秽的想法吧~ 17 | 好的~准备好被狠狠的捉弄吧♥~笨蛋 18 | 欸♥~几天不见真的是越来越杂鱼和恶心了♥~ 19 | 笨蛋♥~ 20 | 那也太杂鱼了吧~ 21 | -------------------------------------------------------------------------------- /Assets/中药.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/中药.xlsx -------------------------------------------------------------------------------- /Assets/喜报.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/喜报.jpg -------------------------------------------------------------------------------- /Assets/柴郡猫猫/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/柴郡猫猫/1.jpg -------------------------------------------------------------------------------- /Assets/柴郡猫猫/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/柴郡猫猫/2.jpg -------------------------------------------------------------------------------- /Assets/柴郡猫猫/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/柴郡猫猫/3.jpg -------------------------------------------------------------------------------- /Assets/柴郡猫猫/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/柴郡猫猫/4.jpg -------------------------------------------------------------------------------- /Assets/柴郡猫猫/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/柴郡猫猫/5.jpg -------------------------------------------------------------------------------- /Assets/柴郡猫猫/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/柴郡猫猫/6.jpg -------------------------------------------------------------------------------- /Assets/柴郡猫猫/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/柴郡猫猫/7.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_10@13-01-2022_01-46-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_10@13-01-2022_01-46-09.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_11@13-01-2022_01-46-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_11@13-01-2022_01-46-09.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_12@26-05-2022_14-12-55.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_12@26-05-2022_14-12-55.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_13@26-05-2022_14-13-47.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_13@26-05-2022_14-13-47.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_1@13-01-2022_01-26-54.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_1@13-01-2022_01-26-54.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_2@13-01-2022_01-46-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_2@13-01-2022_01-46-09.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_3@13-01-2022_01-46-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_3@13-01-2022_01-46-09.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_4@13-01-2022_01-46-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_4@13-01-2022_01-46-09.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_5@13-01-2022_01-46-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_5@13-01-2022_01-46-09.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_6@13-01-2022_01-46-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_6@13-01-2022_01-46-09.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_7@13-01-2022_01-46-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_7@13-01-2022_01-46-09.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_8@13-01-2022_01-46-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_8@13-01-2022_01-46-09.jpg -------------------------------------------------------------------------------- /Assets/疯狂星期四/photos/photo_9@13-01-2022_01-46-09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/Assets/疯狂星期四/photos/photo_9@13-01-2022_01-46-09.jpg -------------------------------------------------------------------------------- /DEV.md: -------------------------------------------------------------------------------- 1 | # 写在前面 2 | 3 | irori面向过程,代码风格**非常扭曲**,如果你不喜欢看长长长长的函数定义和大dict的话请善用代码折叠和查找功能 4 | 5 | 比如vscode在每个函数左边有个小箭头 6 | 7 | 8 | # 项目结构 9 | 10 | | 文件 | 存在意义 | 11 | | --- | --- | 12 | | authdata | 指定登录必须的认证信息和mirai-http-api地址 | 13 | | cfg.json | 全局策略设置文件,参见README.md | 14 | | irori.py | 程序的主入口 | 15 | | Fetcher.py | 爬虫函数包 | 16 | | Utils.py | 功能性函数包(待拆分) | 17 | | Callable.py | 热重载的核心,将扫描到的插件导入到命令映射表 | 18 | | Routiner.py | 订阅业务和定时任务的核心 | 19 | | Sniffer.py | 监听器消息处理核心 | 20 | | plugins/ | 插件文件夹 | 21 | | config.py | 仅仅是用于docker快速部署用到的小脚本,与主程序无关 | 22 | 23 | # 基本原理 24 | 25 | 程序的入口是[irori.py](irori.py),每次的消息会走进那里的监听器。 26 | 27 | 对于每条消息,她会将其以空格隔开,将双减号开头的参数解析并塞入extDict然后查找[Callable.py](Callable.py)里面有没有对应的命令,如果有则调用然后退出,没有则下放到查找有没有满足条件的sniffer。 28 | 29 | `Callable.py`是热重载的核心,每次收到热重载命令会重载这个文件。所以其实只有Callable里面涉及到的包会重载,即plugins目录下的所有插件以及Callable本身。 30 | 31 | `Callable.py`执行的工作是只是将plugins下所有业务代码载入命令映射表。命令调用的主题在入口irori.py处进行 32 | 33 | 项目本身包含9个自带插件,可以用#h查看 34 | 35 | # 我就是想让她动起来 36 | 37 | 我们在[plugins/](plugins/)下建一个py文件,比如[myplugin.py](plugins/myplugin.py)然后照着以下这么写 38 | 39 | ```python 40 | async def 复读(*attrs, kwargs={}): 41 | return ' '.join(attrs) # 现在也可以直接返回一个整数或者字符串 42 | ``` 43 | 44 | 这样你就可以直接使用命令`#复读`来调用`复读`这个函数 45 | 46 | 重启`irori.py`或者使用`sudo reload`,然后向你的bot发送`#复读 2333`,不出意外的话bot会复读`2333`。 47 | 48 | # 我想让别人知道我的命令怎么用 49 | 50 | 只需要在我们的例子中,为业务函数加上`__doc__`即文档字符串即可。具体操作如下: 51 | 52 | ```python 53 | from graia.application.message.elements.internal import Plain 54 | 55 | async def 复读(*attrs, kwargs={}): 56 | """这是一个复读命令 57 | 用法: 58 | #复读 [某些消息]""" 59 | return [Plain(' '.join(attrs))] 60 | ``` 61 | 62 | 这样一来使用时就可以通过`#h #复读`来查询到这个命令的帮助了。 63 | 64 | # 我同一个函数想使用多个命令来调用 65 | 66 | 我们可以在函数下方为函数指定属性`SHORTS`,如下: 67 | 68 | ```python 69 | from graia.application.message.elements.internal import Plain 70 | 71 | async def 复读(*attrs, kwargs={}): 72 | """这是一个复读命令 73 | 用法: 74 | #复读 [某些消息]""" 75 | return [Plain(' '.join(attrs))] 76 | 77 | 复读.SHORTS = ['#rep'] 78 | ``` 79 | 80 | `SHORTS`属性必须是一个列表,里面提供的所有字符串会被alias到这个函数里。注意这里不能像函数名一样忽略开头的#号,否则如上打成`rep`则消息需以`rep 2333`的形式才能触发上述效果 81 | 82 | 83 | ## *attrs 84 | 85 | 如果你没用过星号表达式,那你应该先百度一下( 86 | 87 | 这里的`*attrs`装的是命令参数,irori会将每条消息按空格拆开,如下图所示: 88 | 89 | ![mhtjtmslm](Assets/mhtslm.png) 90 | 91 | 各个参数会依次塞入`*attrs`,它是一个不定长的tuple,请灵活运用。 92 | 93 | ## kwargs 94 | 95 | 捕获一些额外信息的不定长字典,常用的有: 96 | 97 | `player`:发送对象的player号 98 | 99 | `mem`:发送对象的QQ,是一个Member对象,可以通过.id拿到qq号 100 | 101 | `gp`:只在群组消息会有,消息来源群的群对象(Group),可以通过.id拿到群号 102 | 103 | `pic`:消息中包含的第一张图片的url 104 | 105 | 还有各种带`-`号的可选参数,参见[可选参数](可选参数) 106 | 107 | ## 关于player号 108 | 109 | player号事irori中根据群或者好友来源qq号生成的一种统一号。用于消息分发等地方。算法事好友消息即好友的qq号,群组消息则事群组qq号加上1<<39 110 | 111 | 如果有获取player号的需求,直接调用Utils下的getPlayer方法,将**kwargs直接扔进去即可 112 | 113 | 如: 114 | 115 | ```python 116 | player = getPlayer(**kwargs) 117 | ``` 118 | 119 | ## 键值参数 120 | 121 | 在消息的预处理中,每段以`--`开头的参数不会被扔进`*attrs`里。它们会被装进**kwargs里以键值对的形式传递。参见以下几个例子: 122 | 123 | ``` 124 | #线代 mul 1,1,4;5,1,4;1,9,1 2,2,3;4,4,5;3,3,1 --force-image 125 | ``` 126 | 127 | 此时`*attrs`里装的是 128 | ```python 129 | ("mul","1,1,4;5,1,4;1,9,1","2,2,3;4,4,5;3,3,1") 130 | ``` 131 | 132 | `**kwargs`里装有 133 | ```python 134 | {"-force-image":""} 135 | ``` 136 | 137 | ``` 138 | #线代 mul 1,1,4;5,1,4;1,9,1 --force-image --theme=114 2,2,3;4,4,5;3,3,1 139 | ``` 140 | 141 | 此时`*attrs`里装的是 142 | ```python 143 | ("mul","1,1,4;5,1,4;1,9,1","2,2,3;4,4,5;3,3,1") 144 | ``` 145 | 146 | `**kwargs`里装有 147 | ```python 148 | {"-force-image":"","-theme":"114"} 149 | ``` 150 | 151 | 请灵活使用这种参数 152 | 153 | ## 登录任务 154 | 155 | 这个请直接在`irori.py`的`hajime`函数下面加 156 | 157 | # Utils相关 158 | 159 | ## 异步消息分发 160 | 161 | 可以使用Utils下的msgDistributer异步方法 162 | 163 | 它接受不定长键值型传参 164 | 165 | 下面给出了几个例子: 166 | 167 | ```python 168 | asyncio.ensure_future(msgDistributer(msg="文字",typ="P",player=114514)) 169 | ``` 170 | 171 | 向player号为114514的老铁发送`文字`这么个文本消息 172 | 173 | ```python 174 | asyncio.ensure_future(msgDistributer(msg="kuaikule",typ="E",player=114514)) 175 | ``` 176 | 177 | ![kuaikule](Assets/kuaikule.png) 178 | 179 | ```python 180 | asyncio.ensure_future(msgDistributer(msg="https://i.pximg.net/img-original/img/2020/09/27/19/46/09/84651430_p0.jpg",typ="I",player=114514)) 181 | ``` 182 | 183 | 发![夸的新衣服](https://i.pximg.net/img-original/img/2020/09/27/19/46/09/84651430_p0.jpg)这么一张图(挺灵车的功能 184 | 185 | ```python 186 | asyncio.ensure_future(msgDistributer(msg="",typ="I",player=114514)) 187 | ``` 188 | 189 | 以base64编码形式发送图片 190 | 191 | ```python 192 | asyncio.ensure_future(msgDistributer(msg="/home/irori/wakaru.png",typ="I",player=114514)) 193 | ``` 194 | 195 | 以本地文件目录的形式发送图片 196 | 197 | ## 文件操作 198 | 199 | 因为v3的限制,Image大概只能本地文件发送,可以使用Utils下的randstr生成临时文件名,参数为随机字符串长度,然后将文件名扔入rmTmpFile异步方法让这个临时文件在一段时间后删除 200 | 201 | 例: 202 | 203 | ```python 204 | fn = 'tmp' + randstr(8) 205 | with open(fn,'w') as f: 206 | f.write("something\n") 207 | asyncio.ensure_future(rmTmpFile(fn)) 208 | return Image.fromFileSystem(fn) 209 | ``` 210 | 211 | ## 使用sniffer 212 | 213 | sniffer是检测消息是否存在触发的关键词来决定对应函数要不要被调用的一种机制,过多的sniffer会降低程序处理速度,因为对于每条消息都要O(n)检索 214 | 215 | 新建一个sniffer,可以使用Utils下的overwriteSniffer方法,用例: 216 | 217 | ```python 218 | overwriteSniffer(114514,'#repeat','.*','复读:') 219 | ``` 220 | 221 | 解释一下: 222 | 223 | + 第一个参数是player,即需要添加sniffer的player号,是int 224 | 225 | + 第二个参数是满足条件后调用的命令 226 | 227 | + 第三个参数是一个正则串,如果发送的消息能被这个正则串匹配,则会触发这个方法 228 | 229 | + 之后的参数是不定长参数,他们会添加于命令之后,然后再将源消息当做参数加入 230 | 231 | 比如假设你执行了上面的一句 232 | 233 | 然后你向irori发送`LALALA 233`这么一条文本消息,由于`.*`被匹配成功,bot处理的时候会视为这么一条消息: 234 | 235 | ``` 236 | #repeat 复读: LALALA 233 237 | ``` 238 | 239 | 如果要为一条sniffer添加一个监听关键词,可以使用Utils下的appendSniffer方法。与overwriteSniffer接受参数唯一的不同就是它没有最后那个不定长参数 240 | 241 | ```python 242 | appendSniffer(114514,'#repeat','\?') 243 | ``` 244 | 245 | 删掉对某个事件的所有sniffer可以使用Utils下的removeSniffer方法。 246 | 247 | ```python 248 | removeSniffer(114514,'#repeat') 249 | ``` 250 | 251 | 其他大可直接参考Utils.py内的函数文档。 252 | 253 | # 发送图片 254 | 255 | 走Utils下的generateImageFromFile方法 256 | 257 | # 全局变量 258 | 259 | 由于到处都有import GLOBAL,所以尽管把需要的东西往GLOBAL里放 260 | 261 | # 对接AVG 262 | 263 | 这是一个大坑,我们的其实还不太成型,这里先不讨论。 264 | 265 | 配置AVG需要在本目录下新建一个`hakushinAVG.txt` 266 | 267 | 然后写上AVG的接口地址,然后换行写上端口 268 | 269 | 比如我是这么写的: 270 | 271 | ``` 272 | 127.0.0.1 273 | 1919 274 | ``` 275 | 276 | 这部分因为耦合度太高之后要大力重构 277 | 278 | # 语音消息相关 279 | 280 | 如果想让irori发送各种语音消息,需要安装`ffmpeg`并且设置好环境变量以便irori调用。 281 | 282 | linux下需要编译安装ffmpeg,以让它支持`amr_nb`编码器。 283 | 284 | 在踩了无数坑之后本仓库下形成了一个debian系列的[一键脚本](ffmpeg-get-install.sh),欢迎取用测试 -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #基于的基础镜像 2 | FROM alpine:latest 3 | 4 | #代码添加到code文件夹,后面可以通过进入容器中看的 5 | # COPY ./ /irori 6 | 7 | RUN apk update && apk add python3 && apk add py3-pip && \ 8 | apk add make automake gcc g++ subversion python3-dev && \ 9 | apk add curl bash openjdk8-jre-base && \ 10 | apk add jpeg-dev zlib-dev unzip screen nano git && \ 11 | rm -rf /var/cache/apk/* && \ 12 | pip3 install -U pip && \ 13 | pip3 install wheel && \ 14 | git clone https://github.com/voidf/bot_irori.git /irori && \ 15 | wget http://d0.ananas.chaoxing.com/download/e42506778079d4a29bf70982a8d69912 -U "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" -O env.zip && \ 16 | pip3 install -r /irori/requirements.txt && \ 17 | unzip -d /env env.zip && pwd && ls -al && cd env && chmod +x run.sh 18 | 19 | # 设置code文件夹是工作目录 20 | WORKDIR /irori 21 | 22 | ENV JAVA_HOME /usr/lib/jvm/default-jvm 23 | ENV PATH ${PATH}:${JAVA_HOME}/bin 24 | 25 | #当容器启动时,使用python3执行指定路径的py脚本 26 | CMD ["python3", "config.py"] -------------------------------------------------------------------------------- /Dockerfile-CN: -------------------------------------------------------------------------------- 1 | #基于的基础镜像 2 | FROM alpine:latest 3 | 4 | #代码添加到code文件夹,后面可以通过进入容器中看的 5 | # COPY ./ /irori 6 | 7 | RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \ 8 | apk update && apk add python3 && apk add py3-pip && \ 9 | apk add make automake gcc g++ subversion python3-dev && \ 10 | apk add curl bash openjdk8-jre-base && \ 11 | apk add jpeg-dev zlib-dev unzip screen nano git && \ 12 | rm -rf /var/cache/apk/* && \ 13 | pip3 install -U pip -i https://pypi.tuna.tsinghua.edu.cn/simple/ && \ 14 | pip3 install wheel -i https://pypi.tuna.tsinghua.edu.cn/simple/ && \ 15 | git clone https://github.com/voidf/bot_irori.git /irori && \ 16 | wget http://d0.ananas.chaoxing.com/download/e42506778079d4a29bf70982a8d69912 -U "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" -O env.zip && \ 17 | pip3 install -r /irori/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/ && \ 18 | unzip env.zip && chmod +x /env/run.sh && cd /env 19 | 20 | # 设置code文件夹是工作目录 21 | WORKDIR /irori 22 | 23 | ENV JAVA_HOME /usr/lib/jvm/default-jvm 24 | ENV PATH ${PATH}:${JAVA_HOME}/bin 25 | 26 | #当容器启动时,使用python3执行指定路径的py脚本 27 | CMD ["python3", "config.py"] -------------------------------------------------------------------------------- /OS_requirements.txt: -------------------------------------------------------------------------------- 1 | # quic depends 2 | # aioquic 3 | 4 | 5 | # adapters 6 | # prompt_toolkit 7 | 8 | # server 9 | brotlipy 10 | aiohttp 11 | python-multipart 12 | uvicorn 13 | 14 | # worker 15 | jieba 16 | pillow 17 | pydantic 18 | brotli 19 | requests[socks] 20 | bs4 21 | numpy 22 | qrcode 23 | selenium 24 | json5 25 | mido 26 | pexpect 27 | devtools 28 | xlrd==1.2.0 29 | 30 | # debug 31 | loguru 32 | 33 | # Math 34 | numpy 35 | colorclass 36 | terminaltables 37 | scikit-fuzzy 38 | matplotlib 39 | 40 | # irori distribution system 41 | # Server `apt-get install rabbitmq-server` 42 | # for windows `choco install rabbitmq` 43 | # using command `celery -A test3_celery -P eventlet` 44 | # or 45 | # import os 46 | # os.environ.setdefault('FORKED_BY_MULTIPROCESSING', '1') 47 | # 猜解文件类型,win下需要pip install python-magic-bin, debian下sudo apt-get install libmagic1 48 | python-magic 49 | 50 | mongoengine 51 | markdown 52 | celery 53 | fastapi 54 | flower # celery -A modulo flower 55 | python-jose[cryptography] 56 | passlib[bcrypt] -------------------------------------------------------------------------------- /QUICAdapterTerm.py: -------------------------------------------------------------------------------- 1 | import aioquic 2 | 3 | import json 4 | import logging 5 | import asyncio 6 | from typing import NoReturn 7 | from basicutils.task import ArgumentParser 8 | from loguru import logger 9 | import sys 10 | 11 | logger.add(lambda msg: print()) 12 | 13 | # logger.add(sys.stdout, format='>') 14 | from prompt_toolkit.patch_stdout import patch_stdout 15 | from prompt_toolkit.shortcuts import PromptSession 16 | 17 | logging.basicConfig( 18 | level=logging.DEBUG, 19 | format='%(asctime)s<%(filename)s:%(lineno)d>[%(levelname)s]%(message)s', 20 | datefmt='%H:%M:%S' 21 | ) 22 | 23 | import cfg 24 | 25 | sport = cfg.quic_port 26 | hostname = cfg.quic_host 27 | 28 | from basicutils.network import * 29 | from basicutils.quic import * 30 | 31 | class QUICTerminalSession(QUICSessionBase): 32 | def initialize(self): 33 | pass 34 | async def pulling_loop(self): 35 | while 1: 36 | ent = await self.recv() 37 | plaintext = ent.chain.onlyplain() 38 | if plaintext: 39 | logger.info('文本内容:\n{}', plaintext) 40 | sysinfo = ent.unpack_rawstring() 41 | if sysinfo: 42 | logger.warning('系统消息:\n{}', sysinfo) 43 | async def command_loop(self, syncid: str): 44 | self.syncid = syncid 45 | asyncio.ensure_future(self.pulling_loop()) 46 | app = ArgumentParser('command') 47 | app.add_argument('cmd', choices=[ 48 | 'send', 'raw', 'hex', 'eval', 'sudo' 49 | ]) 50 | session = PromptSession('(irori - Terminal)>') 51 | while 1: 52 | try: 53 | cmdlist = (await session.prompt_async()).split() 54 | session.message = '(irori - Terminal OwO)>' 55 | if not cmdlist: 56 | continue 57 | cmd, *ato = cmdlist 58 | logger.debug(ato) 59 | 60 | if cmd == 'send': 61 | ent = CoreEntity( 62 | chain=MessageChain.auto_make(' '.join(ato)), 63 | player=playerid, 64 | source=syncid, 65 | meta={} 66 | ) 67 | logger.debug('SEND {}', ent.json()) 68 | await self.send(ent) 69 | elif cmd == 'sudo': 70 | ent = CoreEntity( 71 | chain=MessageChain.auto_make(' '.join(['sudo'] + ato)), 72 | player=playerid, 73 | source=syncid, 74 | meta={} 75 | ) 76 | logger.debug('SEND {}', ent.json()) 77 | await self.send(ent) 78 | # elif cmd == '' 79 | 80 | except KeyboardInterrupt: 81 | break 82 | except: 83 | logger.debug(traceback.format_exc()) 84 | session.message = '(irori - Terminal QwQ)>' 85 | app.print_help() 86 | # print("send") 87 | 88 | 89 | 90 | import traceback 91 | 92 | import ssl 93 | from aioquic.quic.configuration import QuicConfiguration 94 | from aioquic.h3.connection import H3_ALPN 95 | from aioquic.asyncio.client import connect 96 | 97 | syncid: str 98 | playerid: str 99 | 100 | async def run(): 101 | with patch_stdout(): 102 | conf = QuicConfiguration( 103 | alpn_protocols=H3_ALPN, 104 | is_client=True, 105 | max_datagram_frame_size=cfg.buffer, 106 | idle_timeout=cfg.idle_tle, 107 | verify_mode=ssl.CERT_NONE 108 | # quic_logger=logger.Logger, 109 | # secrets_log_file=secrets_log_file, 110 | ) 111 | loop = asyncio.get_running_loop() 112 | print(hostname, sport) 113 | # while 1: 114 | async with connect( 115 | cfg.quic_host, 116 | cfg.quic_port, 117 | configuration=conf 118 | ) as C: 119 | reader, writer = await C.create_stream() 120 | 121 | writer.write(f'A {cfg.quic_admin_key} {playerid}'.encode('utf-8')) 122 | 123 | ses = QUICTerminalSession(reader, writer) 124 | syncid = (await ses.recv()).unpack_rawstring() 125 | logger.critical("您的终端号:{}", syncid) 126 | await ses.command_loop(syncid) 127 | 128 | 129 | 130 | 131 | if __name__ == "__main__": 132 | playerid = 'T' + input('随便取一个playerid?>>>') 133 | loop = asyncio.get_event_loop() 134 | loop.run_until_complete(run()) 135 | -------------------------------------------------------------------------------- /QUICFaker.py: -------------------------------------------------------------------------------- 1 | 2 | class Configuration(): 3 | # Server 4 | bind_port = 18082 5 | buffer = 4096 6 | idle_tle = 70 7 | heartbeat_time = 30 8 | quic_key = 'public' 9 | quic_admin_key = 'admin' 10 | # Worker 11 | quic_port = 18082 12 | quic_host = '127.0.0.1' 13 | max_worker = 4 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 人懒,问就是女生自用 2 | 3 | 由于本项目重构后部署过于复杂,如果你真的想部署一个irori,建议直接开Issue问,而不是看这个我半年才更新一次的Readme。 4 | 5 | ![头像](Assets/E2.png) 6 | 7 | # bot-irori 8 | 9 | 一个命令式,主要支援文字形式信息处理的服务接口。 10 | 11 | 同时提供mirai-http-api接口和websocket外部应用接口。 12 | 13 | ## features 14 | 15 | ### 面向用户 16 | 17 | - [x] 每日求签~~算命~~ 18 | - [x] 答案之书(不知道为什么群友总能玩出新花样 19 | - [x] 谷歌翻译,百度翻译(fufu提供的爬虫 20 | - [x] ddl事件安排及提醒 21 | - [x] 搜歌 22 | - [x] 表达式即时求值的计算器 23 | - [x] LaTeX公式渲染(其实是爬虫 24 | - [x] CodeForces、AtCoder、牛客、力扣、洛谷的比赛提醒推送 25 | - [x] 搜番 26 | - [x] 每日天气预报 27 | 28 | 完整功能请部署后使用#h查看 29 | 30 | ### 面向开发 31 | 32 | - [x] 调试友好的远程系统命令调用(eval, exec, os.popen) 33 | - [x] 代码热重载 34 | - [x] 方便、可扩展的命令编写 35 | - [x] 保留消息链实现,支援多媒体信息 36 | - [x] 中心Server,多Worker,消息队列部署任务架构,可胜任计算密集任务并发处理,任务超时自动杀死 37 | 38 | ## requirements 39 | 40 | ### MongoDB 41 | 42 | 安装运行服务启动即可,如需网络访问建议弄个数据库级别的登录认证。 43 | 44 | ### RabbitMQ 45 | 46 | 由Celery依赖,部署完毕启动即可。 47 | 48 | ### Python3.8+ 49 | 50 | 以后可能会引入match语法而需要3.10+ 51 | 52 | 依赖包参见[OS_requirements.txt](OS_requirements.txt) 53 | 54 | 配置最麻烦的模块是[celery](https://github.com/celery/celery),还请参见其仓库的指南。 55 | 56 | ### Mirai 2.0+(QQbot必须) 57 | 58 | 别忘了装上配套的mirai-http-api插件 59 | 60 | ## deployment 61 | 62 | 服务器节点的入口是[Server.py](Server.py), 直接使用`python3 Server.py`即可运行 63 | 64 | 执行模块节点的入口是[Worker.py](Worker.py), 使用`celery -A Worker worker`运行 65 | 66 | ### cfg.py配置模板 67 | 68 | ```python 69 | # 数据库连接认证一条龙串 70 | db = {"host": "mongodb://user:password@localhost:27017/irori_data?authSource=yourauthsource"} 71 | # 百度翻译用的api接口 72 | baidu_appid = "114514" 73 | baidu_secretKey = "1919810" 74 | 75 | web_host = '0.0.0.0' # Server监听网卡 76 | web_port = 41919 # Server监听端口 77 | dist_host = 'http://127.0.0.1' # Worker访问Server的地址,注意不要加上尾随/ 78 | ``` 79 | 80 | Server和Worker都启动成功后,浏览器访问Server监听的网口,转到/docs路径,用屏幕打印的uuid进行身份验证(用户名和密码都填uuid) 81 | 82 | 然后若要对接mirai-http-api,在/auth/mirai接口处填入其ws链接 83 | 84 | 若要使用ws连接,请向/ws发送请求。 85 | 86 | (没更完,写累了) 87 | 88 | ## 系统命令手册 89 | 90 | sudo 系列命令只有在消息的发送来源包括在auth_masters内的时候会执行。 91 | 92 | 有关auth_masters的配置,请在数据库中的irori_config的collection下直接配置。 93 | 94 | | 命令 | 描述 | 95 | | ------ | ----- | 96 | | sudo exec | 执行一条python exec语句,获取其stdout | 97 | | sudo eval | 执行一条python语句,并返回结果(由于不支持赋值等无返回值操作,故提供exec) | 98 | | sudo run | 在宿主机上运行一条shell命令 | 99 | 100 | 101 | ## 参与开发 102 | 103 | 参见[DEV.md](DEV.md)或者本仓库的Wiki 104 | 105 | ## TODOs: 106 | 107 | + 小坑 108 | 109 | + [ ] 实现生命棋 110 | + [ ] 实现Fygon计算复杂度 111 | + [ ] 求二次剩余 112 | + [ ] 选课 113 | + [ ] 对接屑站转发抽奖 114 | + [ ] 自用本校工具箱 115 | + [ ] 百度TTS和谷歌TTS 116 | + [ ] 重构CF爬虫 117 | + [ ] 增加MML合成音频标准 118 | 119 | + 大坑 120 | 121 | + [ ] 完善暂时闭源的AVG然后一起开源 122 | + [ ] ai棋牌 123 | + [ ] TRPG 124 | + [x] irori-OpenJudge(但是自建果然不如对接 125 | + [ ] irori农场( 126 | + [ ] 文档重构 127 | + [ ] TG消息同步和推送 128 | 129 | + 卫星 130 | 131 | + [ ] 《奇点》 132 | + [ ] irori前端面板 133 | + [ ] 聊天机器人 134 | + [ ] 形象重绘 ~~机伤网站关了(悲~~ 135 | + [ ] ~~出道~~ 136 | 137 | ## 引用项目: 138 | 139 | [Kumbong/quine_mccluskey](https://github.com/Kumbong/quine_mccluskey) 140 | 141 | [be5invis/Sarasa-Gothic](https://github.com/be5invis/Sarasa-Gothic) 142 | 143 | ## 其它引用资源(侵删) 144 | 145 | [telegram: 疯狂星期四文案收集姬](https://t.me/crazythu) 146 | 147 | ## irori的好朋友 148 | 149 | [ssttkkl/PixivBot](https://github.com/ssttkkl/PixivBot) 150 | 151 | [KutouAkira/bot_fufu](https://github.com/KutouAkira/bot_fufu) 152 | 153 | # 妙妙屋沙雕日常 154 | 155 | > 不会吧不会吧,不会真的有人什么都问答案之书吧? 156 | 157 | ![](Assets/kusa/1.png) 158 | 159 | ![](Assets/kusa/2.jpg) 160 | 161 | ![](Assets/kusa/3.jpg) 162 | 163 | ![](Assets/kusa/4.jpg) 164 | 165 | ![](Assets/kusa/5.jpg) 166 | 167 | ![](Assets/kusa/6.png) 168 | 169 | ![](Assets/kusa/7.jpg) 170 | 171 | ![](Assets/kusa/8.png) 172 | 173 | ![](Assets/kusa/9.png) 174 | 175 | ![](Assets/kusa/10.png) 176 | 177 | ![](Assets/kusa/11.png) 178 | 179 | ![](Assets/kusa/12.png) 180 | 181 | ![](Assets/kusa/13.png) 182 | 183 | ![](Assets/kusa/14.png) 184 | 185 | ![](Assets/kusa/15.png) 186 | 187 | ![](Assets/kusa/16.png) 188 | 189 | ![](Assets/kusa/17.png) 190 | 191 | ![](Assets/kusa/18.png) 192 | 193 | ![](Assets/kusa/19.png) 194 | 195 | ![](Assets/kusa/20.png) 196 | 197 | **欢迎投稿(** 198 | 199 | # 本项目采用AGPLv3.0协议开源 200 | -------------------------------------------------------------------------------- /RunCommand.txt: -------------------------------------------------------------------------------- 1 | uvicorn main:app --host 0.0.0.0 --port 5000 --reload --no-use-colors --ssl-keyfile localhost_key.pem --ssl-certfile localhost_chain.crt -------------------------------------------------------------------------------- /Server.py: -------------------------------------------------------------------------------- 1 | import loguru 2 | from fapi.models.Routiner import * 3 | from fastapi import FastAPI, Depends 4 | from loguru import logger 5 | logger.add("Server.log", rotation="20 MB") 6 | from cfg import db, web_host, web_port 7 | 8 | from fapi.models.Auth import * 9 | # import fapi.G 10 | import os 11 | import sys 12 | 13 | sys.dont_write_bytecode = True 14 | 15 | def create_fastapi() -> FastAPI: 16 | from mongoengine import connect 17 | app = FastAPI(version="2.0.0", title="Irori distributed system") 18 | connect(**db) 19 | uuidtoken = IroriUUID.objects() 20 | if not uuidtoken: 21 | uuidtoken = str(uuid.uuid4()) 22 | IroriUUID(uuid=uuidtoken).save() 23 | else: 24 | uuidtoken = uuidtoken.first().uuid 25 | logger.critical(f'token: {uuidtoken}') 26 | 27 | if not IroriConfig.objects(): 28 | IroriConfig().save() 29 | 30 | if os.getcwd() not in sys.path: 31 | sys.path.append(os.getcwd()) 32 | print(sys.path) 33 | 34 | from fapi.routers import master_router 35 | app.include_router(master_router) 36 | 37 | return app 38 | 39 | 40 | app = create_fastapi() 41 | 42 | 43 | @app.on_event('startup') 44 | async def startup_coroutines(): 45 | from fapi.models.FileStorage import TempFile 46 | from fapi.routers.auth import connect_mirai 47 | 48 | asyncio.gather(*( 49 | connect_mirai(i['miraiwsurl']) 50 | for i in IroriConfig.objects().first().startup_connect_actions 51 | )) # 先接入预设的输出会话 52 | 53 | asyncio.create_task(Routiner.recover_routiners()) # 不需要返回值的进队之后再执行 54 | asyncio.create_task(TempFile.resume()) 55 | 56 | logger.debug("【TASKS】") 57 | logger.debug(asyncio.all_tasks()) 58 | # if os.path.exists('startup_actions.json'): 59 | # import json 60 | # with open('startup_actions.json', 'r') as f: 61 | # j = json.load(f) 62 | # await connect_mirai(j['miraiwsurl']) 63 | 64 | 65 | if __name__ == '__main__': 66 | import uvicorn 67 | uvicorn.run( 68 | app, 69 | host=web_host, 70 | port=web_port, 71 | ) 72 | -------------------------------------------------------------------------------- /SocketServer.py: -------------------------------------------------------------------------------- 1 | import socket, ssl 2 | import websockets 3 | import logging 4 | import asyncio 5 | import sys 6 | import random 7 | from taskutils import ArgumentParser 8 | from typing import * 9 | 10 | logging.basicConfig( 11 | level=logging.DEBUG, 12 | format='%(asctime)s<%(filename)s:%(lineno)d>[%(levelname)s]%(message)s', 13 | datefmt='%H:%M:%S' 14 | ) 15 | 16 | 17 | 18 | busy_pool = {} 19 | worker_pool = {} 20 | 21 | worker_alias = {} 22 | name_pool = set() 23 | 24 | terminals = {} 25 | 26 | import json 27 | import os 28 | from socketutils import * 29 | 30 | with open('cfg.json', 'r',encoding='utf-8') as f: 31 | jj = json.load(f) 32 | wsuri = jj['ws'] 33 | sport = jj['socket_port'] 34 | cport = jj['control_port'] 35 | del jj 36 | 37 | async def main(): 38 | # async def handle_ws_inbound(ws: websockets.WebSocketCommonProtocol, path: str): 39 | async with websockets.connect(wsuri) as websocket: 40 | logging.info("LOGGED INTO MIRAI WS") 41 | 42 | # async def reply(msgbody: dict, msg: str) -> NoReturn: 43 | # msgbody[] 44 | 45 | async def pull_event() -> NoReturn: 46 | while 1: 47 | # await websocket.send("Hello world!") 48 | wsmsg: bytes = await websocket.recv() 49 | jsoncontent = json.loads(wsmsg.decode('utf-8')) 50 | msgbody = jsoncontent.get('data', {}) 51 | if not msgbody: 52 | logging.critical("no response body!") 53 | logging.critical(wsmsg) 54 | continue 55 | 56 | logging.warning(wsmsg) 57 | sid: int = int(jsoncontent['syncId']) 58 | data: dict = jsoncontent['data'] 59 | if sid in terminals: 60 | terminals[sid].write(json.dumps(data).encode('utf-8')) 61 | await terminals[sid].drain() 62 | 63 | elif sid == -1: 64 | if not worker_pool: 65 | await reply(msgbody, "没有可用的worker,请稍后再试") 66 | else: 67 | wid = random.choice(list(worker_pool)) 68 | 69 | 70 | 71 | async def handle_inbound( 72 | reader: asyncio.StreamReader, 73 | writer: asyncio.StreamWriter 74 | ): 75 | data = await reader.read(1<<16) 76 | message = data.decode() 77 | role, *args = message.split(' ') 78 | addr = writer.get_extra_info('peername') 79 | 80 | async def worker(): 81 | worker_pool[addr] = (reader, writer) 82 | new_name = name_pool.pop() 83 | worker_alias[new_name] = addr 84 | logging.info(f"new worker standby ... {new_name} => {addr}") 85 | async def terminal(): 86 | logging.info(f"new terminal standby ... => {addr}") 87 | ap = ArgumentParser() 88 | ap.add_argument('cmd', default='help', choices=['exec', 'eval', 'run', 'send'], help="欢迎使用irori终端,以上是目前支持的命令,输入help或未匹配上面的命令会出现此提示。") 89 | async def sys_exec(args: list): return f"""{exec(' '.join(args))}""" 90 | async def sys_eval(args: list): return f"""{eval(' '.join(args))}""" 91 | async def sys_run(args: list): return f"""{os.popen(' '.join(args)).read()}""" 92 | async def sys_help(args: list): return '' 93 | async def sys_send(args: list): 94 | await websocket.send(' '.join(args)) 95 | return '' 96 | switcher_t = { 97 | 'exec': sys_exec, 98 | 'eval': sys_eval, 99 | 'run': sys_run, 100 | 'send': sys_send 101 | } 102 | syncid = random.randint(0, (1<<31) - 1) 103 | while syncid in terminals: 104 | syncid = random.randint(0, (1<<31) - 1) 105 | 106 | terminals[syncid] = writer 107 | try: 108 | while 1: 109 | data = await reader.read(1<<16) 110 | if len(data) == 0: 111 | terminals.pop(syncid) 112 | break 113 | c, ato = ap.parse_known_args(data.decode('utf-8').split(' ')) 114 | resp: str = await switcher_t.get(c.cmd, sys_help)(ato) 115 | if not resp: 116 | resp = 'No returns' 117 | logging.critical(resp) 118 | writer.write(resp.encode('utf-8')) 119 | await writer.drain() 120 | except Exception as e: 121 | terminals.pop(syncid) 122 | raise e 123 | 124 | switcher = { 125 | 'worker': worker, 126 | 'terminal': terminal 127 | } 128 | asyncio.ensure_future( 129 | switcher[role]() 130 | ) 131 | server = await asyncio.start_server(handle_inbound, '0.0.0.0', sport) 132 | monitor = await asyncio.start_server(handle_inbound, '0.0.0.0', cport) 133 | addr = server.sockets[0].getsockname() 134 | logging.info(f'Socket Serving on {addr}') 135 | asyncio.ensure_future(pull_event()) 136 | 137 | 138 | # writer.write(data) 139 | # await writer.drain() 140 | 141 | async with server: 142 | await server.serve_forever() 143 | import string 144 | for digits in string.digits: 145 | for uppers in string.ascii_uppercase: 146 | name_pool.add(digits + uppers) 147 | 148 | asyncio.run(main()) 149 | # ec = zip_msg('AAAAB'*1000) 150 | # print(ec) 151 | # unzip_msg(ec) 152 | # dd = {} 153 | -------------------------------------------------------------------------------- /SocketTerminal.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import traceback 3 | import json 4 | 5 | hostname = 'localhost' 6 | 7 | 8 | with open('cfg.json', 'r',encoding='utf-8') as f: 9 | jj = json.load(f) 10 | sport = jj['socket_port'] 11 | del jj 12 | 13 | with socket.create_connection((hostname, sport)) as sock: 14 | # data = sock.recv(1<<16) 15 | sock.send(b'terminal') 16 | 17 | while 1: 18 | cmd = input("terminal@irori:/#") 19 | sock.send(cmd.encode('utf-8')) 20 | data = sock.recv(1<<16) 21 | if sock._closed: 22 | break 23 | print(data) 24 | 25 | # print(data) 26 | # data = sock.recv(1<<16) 27 | # print(data) 28 | # data = sock.recv(1<<16) 29 | # sock. 30 | # print(data) 31 | 32 | # with context.wrap_socket(sock, server_hostname=hostname) as ssock: 33 | # print(ssock.version()) -------------------------------------------------------------------------------- /SocketWorker.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | import argparse 4 | 5 | hostname = 'localhost' 6 | 7 | with open('cfg.json', 'r',encoding='utf-8') as f: 8 | jj = json.load(f) 9 | sport = jj['socket_port'] 10 | del jj 11 | 12 | import traceback 13 | from multiprocessing.dummy import Pool as Pool2 14 | def task_monitor(func, *args, **kwargs): 15 | tle = kwargs.get('tle', None) 16 | p = Pool2(1) 17 | res = p.apply_async(func, args=args) 18 | pid = kwargs['pid'] 19 | # print(kwargs) 20 | try: 21 | out = res.get(tle) 22 | # kwargs['worker'].release() 23 | 24 | tmpd = kwargs['pcb'][pid] 25 | tmpd['status'] = 'done' 26 | kwargs['pcb'].update({pid:tmpd}) 27 | # kwargs['pcb'][pid].update(status='done') 28 | # print(kwargs) 29 | print(pid, '执行完毕, 结果:', out) 30 | return out 31 | except: 32 | traceback.print_exc() 33 | # kwargs['worker'].release() 34 | 35 | tmpd = kwargs['pcb'][pid] 36 | tmpd['status'] = 'timeout' 37 | kwargs['pcb'].update({pid:tmpd}) 38 | print(pid, '执行超时') 39 | 40 | with socket.create_connection((hostname, sport)) as sock: 41 | # data = sock.recv(1<<16) 42 | sock.send(b'worker') 43 | data = sock.recv(1<<16) 44 | # print(data) 45 | # data = sock.recv(1<<16) 46 | # print(data) 47 | # data = sock.recv(1<<16) 48 | # sock. 49 | # print(data) 50 | 51 | # with context.wrap_socket(sock, server_hostname=hostname) as ssock: 52 | # print(ssock.version()) 53 | 54 | # 1 55 | # 56 | 57 | # 58 | # 60 | # 61 | # 62 | 63 | -------------------------------------------------------------------------------- /TS1.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from typing import Callable, Deque, Dict, List, Optional, Union, cast 3 | 4 | 5 | import aioquic 6 | from aioquic.asyncio import QuicConnectionProtocol, serve 7 | from aioquic.quic.configuration import QuicConfiguration 8 | from aioquic.tls import SessionTicket 9 | 10 | import random 11 | import traceback 12 | 13 | import logging 14 | logging.basicConfig( 15 | level=logging.DEBUG, 16 | format='%(asctime)s<%(filename)s:%(lineno)d>[%(levelname)s]%(message)s', 17 | datefmt='%H:%M:%S' 18 | ) 19 | from loguru import logger 20 | 21 | class SessionTicketStore: 22 | """ 23 | Simple in-memory store for session tickets. 24 | """ 25 | def __init__(self) -> None: 26 | self.tickets: Dict[bytes, SessionTicket] = {} 27 | 28 | def add(self, ticket: SessionTicket) -> None: 29 | self.tickets[ticket.ticket] = ticket 30 | 31 | def pop(self, label: bytes) -> Optional[SessionTicket]: 32 | return self.tickets.pop(label, None) 33 | 34 | import datetime 35 | buffersize = 65536 36 | 37 | class QUICServerSession: 38 | def __init__(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter): 39 | self._reader = reader 40 | self._writer = writer 41 | self._alive = True 42 | self._Q = asyncio.Queue() 43 | asyncio.ensure_future(self.heartbeat()) 44 | asyncio.ensure_future(self.keep_connect()) 45 | 46 | async def heartbeat(self): 47 | try: 48 | while 1: 49 | if not self._alive: 50 | logger.warning('Connection lost') 51 | return 52 | await asyncio.sleep(20) 53 | self._writer.write(b'D') 54 | except: 55 | logger.warning(traceback.format_exc()) 56 | 57 | async def keep_connect(self): 58 | while 1: 59 | res = await self._reader.read(buffersize) 60 | if not res: 61 | self._Q.put_nowait(ConnectionResetError("ConnectionReset")) 62 | self._alive = False 63 | return 64 | if res == b'd': 65 | logger.debug(res) 66 | continue 67 | else: 68 | logger.info(res) 69 | self._Q.put_nowait(res) 70 | 71 | async def send(self, data: str): self._writer.write(data.encode('utf-8')) 72 | async def recv(self) -> bytes: return await self._Q.get() 73 | 74 | worker_pool={} 75 | adapters={} 76 | 77 | async def handle_inbound( 78 | reader: asyncio.StreamReader, 79 | writer: asyncio.StreamWriter 80 | ): 81 | data = await reader.read(65536) 82 | message = data.decode() 83 | logger.info(message) 84 | vkey, *meta = message.split(' ', 2) 85 | if vkey != 'mykey': 86 | logger.critical(f"Detected invalid key {vkey}") 87 | return 88 | ses = QUICServerSession(reader, writer) 89 | 90 | new_name = random.randint(0, 65535) 91 | while new_name in worker_pool: 92 | new_name = random.randint(0, 65535) 93 | worker_pool[new_name] = ses 94 | logger.info(f"new worker standby ... {new_name}") 95 | try: 96 | # raise NameError('233') 97 | while 1: 98 | msg = (await ses.recv()).decode('utf-8') 99 | logger.info('Received: {}', msg) 100 | except: 101 | logger.warning(traceback.format_exc()) 102 | finally: 103 | logger.warning('Discard {} => ', new_name) 104 | worker_pool.pop(new_name, '') 105 | 106 | logger.critical("Session exit!") 107 | 108 | def inbound_wrapper(reader, writer): 109 | try: 110 | asyncio.ensure_future(handle_inbound(reader, writer)) 111 | except: 112 | logger.warning(traceback.format_exc()) 113 | 114 | if __name__ == "__main__": 115 | conf = QuicConfiguration( 116 | is_client=False, 117 | ) 118 | 119 | conf.load_cert_chain('ssl/A.crt', 'ssl/A.key') 120 | 121 | ticket_store = SessionTicketStore() 122 | 123 | server = aioquic.asyncio.serve( 124 | '0.0.0.0', 8110, 125 | configuration=conf, 126 | stream_handler=inbound_wrapper, 127 | session_ticket_fetcher=ticket_store.pop, 128 | session_ticket_handler=ticket_store.add, 129 | retry=False 130 | ) 131 | 132 | logger.info(f'QUIC listening on 0.0.0.0 {8110}') 133 | 134 | loop = asyncio.get_event_loop() 135 | loop.set_debug(True) 136 | loop.run_until_complete(server) 137 | try: 138 | loop.run_forever() 139 | except KeyboardInterrupt: 140 | logger.critical(traceback.format_exc()) 141 | -------------------------------------------------------------------------------- /TS2.py: -------------------------------------------------------------------------------- 1 | import aioquic 2 | 3 | import json 4 | from loguru import logger 5 | import asyncio 6 | from typing import NoReturn 7 | 8 | import logging 9 | logging.basicConfig( 10 | level=logging.DEBUG, 11 | format='%(asctime)s<%(filename)s:%(lineno)d>[%(levelname)s]%(message)s', 12 | datefmt='%H:%M:%S' 13 | ) 14 | buffersize = 65536 15 | 16 | class QUICWorkerSession: 17 | def __init__(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter): 18 | self._reader = reader 19 | self._writer = writer 20 | self._Q = asyncio.Queue() 21 | asyncio.ensure_future(self.keep_connect()) 22 | async def keep_connect(self): 23 | while 1: 24 | res = await self._reader.read(buffersize) 25 | if not res: 26 | raise ConnectionResetError() 27 | if res == b'D': 28 | logger.debug('Heartbeat') 29 | self._writer.write(b'd') # send Heartbeat ACK msg 30 | else: 31 | logger.debug(f'Putting {res} into queue') 32 | self._Q.put_nowait(res) 33 | 34 | async def recv(self) -> bytes: return await self._Q.get() 35 | async def send(self, data: str) -> NoReturn: self._writer.write(data.encode('utf-8')) 36 | 37 | import traceback 38 | 39 | import ssl 40 | from aioquic.quic.configuration import QuicConfiguration 41 | from aioquic.asyncio.client import connect 42 | 43 | @logger.catch 44 | async def run(): 45 | conf = QuicConfiguration( 46 | is_client=True, 47 | verify_mode=ssl.CERT_NONE 48 | ) 49 | loop = asyncio.get_running_loop() 50 | async with connect( 51 | '127.0.0.1', 52 | 8110, 53 | configuration=conf 54 | ) as C: 55 | ses = QUICWorkerSession(*(await C.create_stream())) 56 | await ses.send('mykey') 57 | 58 | while 1: 59 | cmd = await ses.recv() 60 | if cmd == b'kill': 61 | logger.warning('Kill signal detected') 62 | 63 | 64 | if __name__ == "__main__": 65 | loop = asyncio.get_event_loop() 66 | loop.set_debug(True) 67 | loop.run_until_complete(run()) 68 | -------------------------------------------------------------------------------- /TaskPoolSender.py: -------------------------------------------------------------------------------- 1 | from multiprocessing.managers import BaseManager 2 | from queue import Queue 3 | queue = Queue() 4 | class QueueManager(BaseManager): pass 5 | QueueManager.register('get_queue', callable=lambda:queue) 6 | m = QueueManager(address=('localhost', 50000), authkey=b'abracadabra') 7 | m.connect() 8 | queue = m.get_queue() 9 | 10 | from dataclasses import dataclass 11 | import random, string 12 | 13 | @dataclass 14 | class PendingTask(): 15 | rawstr: str 16 | 17 | def randstr(l: int) -> str: return ''.join(random.choices(string.ascii_letters+string.digits,k=l)) 18 | 19 | import time 20 | 21 | while 1: 22 | time.sleep(random.random()) 23 | tp = PendingTask(randstr(6)) 24 | 25 | print(tp) 26 | queue.put(tp) -------------------------------------------------------------------------------- /TaskPoolWorker.py: -------------------------------------------------------------------------------- 1 | from multiprocessing.managers import BaseManager 2 | from queue import Queue 3 | queue = Queue(maxsize=100) 4 | class QueueManager(BaseManager): pass 5 | QueueManager.register('get_queue', callable=lambda:queue) 6 | m = QueueManager(address=('localhost', 50000), authkey=b'abracadabra') 7 | m.connect() 8 | queue = m.get_queue() 9 | 10 | 11 | from dataclasses import dataclass 12 | import random, string 13 | 14 | @dataclass 15 | class PendingTask(): 16 | rawstr: str 17 | 18 | 19 | while 1: 20 | tp = queue.get() 21 | print(tp) -------------------------------------------------------------------------------- /VendorAdapter7007.py: -------------------------------------------------------------------------------- 1 | import fastapi 2 | import uvicorn 3 | from pydantic import BaseModel 4 | from fastapi import FastAPI 5 | import keyboard 6 | import datetime 7 | import time 8 | import win32clipboard as wcb 9 | import win32api 10 | 11 | last_access = datetime.datetime.now() - datetime.timedelta(seconds=7200) 12 | 13 | app = FastAPI() 14 | 15 | class Msg(BaseModel): 16 | m: str 17 | 18 | def get_clipboard_data(): 19 | wcb.OpenClipboard() 20 | # 枚举剪贴板中的所有格式 21 | format = 0 22 | data_map = {} 23 | while True: 24 | format = wcb.EnumClipboardFormats(format) 25 | if not format: 26 | break 27 | try: 28 | data = wcb.GetClipboardData(format) 29 | data_map[format] = data 30 | except Exception as e: 31 | print(f"Error getting data for format {format}: {e}") 32 | wcb.CloseClipboard() 33 | print(data_map) 34 | return data_map 35 | 36 | @app.post('/i') 37 | async def _(msg: Msg): 38 | global last_access 39 | now = datetime.datetime.now() 40 | gap = (now - last_access).total_seconds() 41 | print(gap) 42 | if gap < 72: 43 | rep = '等一下捏,72秒能请求一次,还差{}秒'.format(72 - gap) 44 | print(rep) 45 | return [rep] 46 | last_access = now 47 | 48 | inputmsg = msg.m 49 | print(inputmsg) 50 | time.sleep(0.5) 51 | print('start') 52 | print(wcb.CF_UNICODETEXT) 53 | 54 | wcb.OpenClipboard() # 打开剪贴板 55 | wcb.EmptyClipboard() # 清空剪贴板 56 | wcb.SetClipboardData(wcb.CF_UNICODETEXT, inputmsg) 57 | wcb.CloseClipboard() # 关闭剪贴板 58 | 59 | keyboard.send('shift+esc') 60 | time.sleep(0.1) 61 | keyboard.send('ctrl+v') 62 | time.sleep(0.1) 63 | keyboard.send('enter') 64 | time.sleep(12) 65 | 66 | prev = '' 67 | while 1: 68 | keyboard.send('ctrl+shift+c') 69 | time.sleep(0.3) 70 | curr = get_clipboard_data()[wcb.CF_UNICODETEXT] 71 | if curr == prev: 72 | break 73 | prev = curr 74 | time.sleep(5) 75 | return [prev] 76 | 77 | if __name__ == "__main__": 78 | uvicorn.run(app, port=11111) # 7007 -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/__init__.py -------------------------------------------------------------------------------- /architechture.sai2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/architechture.sai2 -------------------------------------------------------------------------------- /automata_requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp 2 | pywinauto 3 | python-dotenv 4 | psutil 5 | keyboard 6 | pydantic 7 | loguru 8 | pyautogui 9 | -------------------------------------------------------------------------------- /basicutils/__init__.py: -------------------------------------------------------------------------------- 1 | def make_banner(src: str, filler: str ='=') -> str: 2 | l = len(src) 3 | return filler * l + '\n' + src + '\n' + filler * l 4 | 5 | -------------------------------------------------------------------------------- /basicutils/applications/Player.py: -------------------------------------------------------------------------------- 1 | """用户账号管理类""" 2 | from fapi.models.Player import Player 3 | import os 4 | import sys 5 | 6 | if os.getcwd() not in sys.path: 7 | sys.path.append(os.getcwd()) 8 | 9 | import basicutils.CONST as GLOBAL 10 | 11 | 12 | from bs4 import BeautifulSoup 13 | import re 14 | import asyncio 15 | import requests 16 | import json 17 | import random 18 | import urllib 19 | import traceback 20 | import hashlib 21 | import urllib 22 | from basicutils.chain import * 23 | from basicutils.network import * 24 | from basicutils.task import * 25 | 26 | from uuid import uuid4 27 | from basicutils.algorithms import randstr 28 | from fapi.utils.jwt import generate_player_jwt 29 | 30 | def 外部令牌(ent: CoreEntity): 31 | """/token [/tk] 32 | 管理您的外部令牌 33 | 用法: 34 | /token regen 重新生成口令,并使先前的jwt无效化 35 | /token jwt 生成jwt令牌 36 | """ 37 | args = ent.chain.tostr().split(' ') 38 | player = Player.chk(ent.member) 39 | ctoken = player.items.get('token', '') 40 | buf = [] 41 | if ent.pid != ent.member: 42 | buf.append('【警告】不建议在群聊中使用此功能') 43 | if not ctoken or args and args[0] == 'regen': 44 | ctoken = str(uuid4()) 45 | player.items['token'] = ctoken 46 | player.save() 47 | buf.append('【提示】口令已重新生成,注意先前的jwt令牌会全部失效') 48 | if args and args[0] == 'jwt': 49 | buf.append('【重要】请妥善保管您的新jwt令牌:\n'+generate_player_jwt(ent.member)) 50 | 51 | return '\n'.join(buf) -------------------------------------------------------------------------------- /basicutils/applications/Test.py: -------------------------------------------------------------------------------- 1 | """测试类(开发用""" 2 | from basicutils.database import Sniffer 3 | import os 4 | import sys 5 | if os.getcwd() not in sys.path: 6 | sys.path.append(os.getcwd()) 7 | import basicutils.CONST as GLOBAL 8 | import asyncio 9 | from basicutils.network import * 10 | from basicutils.chain import * 11 | 12 | 13 | def 表情符号查询姬(ent: CoreEntity): 14 | """#unicode [] 15 | 查询给定字符串的unicode码 16 | """ 17 | return [Plain(' '.join([str(ord(i)) for i in ent.chain.tostr()] ))] 18 | 19 | async def Unicode测试姬(*attrs,kwargs={}): 20 | s = int(attrs[0]) 21 | e = int(attrs[1]) 22 | s,e = min(s,e),max(s,e) 23 | w = ' '.join(attrs[2:]) 24 | asyncio.ensure_future(fuzzT(kwargs['gp'],s,e,w)) 25 | 26 | def 乒乓球(ent: CoreEntity): 27 | """#ping [] 28 | 用来测试bot有没有在线 29 | """ 30 | if GLOBAL.pingCtr==0: 31 | s = 'pong' 32 | else: 33 | s = f'pong - {GLOBAL.pingCtr}' 34 | GLOBAL.pingCtr+=1 35 | return [Plain(s)] 36 | 37 | def 复读(ent: CoreEntity): 38 | """#echo [] 39 | 原样返回给定字符串 40 | """ 41 | return ent.chain.tostr() 42 | 43 | async def 废话生成器(*attrs,kwargs={}): return [Plain(' '.join(attrs[:-1])*int(attrs[-1]))] 44 | 45 | 46 | def 清空嗅探器(ent: CoreEntity): 47 | """%clear [] 48 | 清空所有本会话Sniffer 49 | """ 50 | Sniffer.drop(ent.pid) 51 | return [Plain("[Sniffer] 清除完毕")] 52 | 53 | 54 | functionDescript = { 55 | '#fuzz': 56 | """ 57 | 【测试用】基本上是用来测试unicode的 58 | 用法: 59 | #fuzz <起始unicode码> <终止unicode码> <额外输出字符> 60 | """, 61 | '#lim':'设置返回的消息长度大于等于多少时,转换为图片发送', 62 | '#echo':'查询当前字符串的unicode码', 63 | '#ping':'', 64 | '#废话':'【测试用】复读某个字符串,一开始是为测量消息最大长度而设计,目前已知私聊字符串最大长度876,群聊32767.用法#废话 <复读字符串> <复读次数>', 65 | } 66 | -------------------------------------------------------------------------------- /basicutils/applications/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/applications/__init__.py -------------------------------------------------------------------------------- /basicutils/media.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from basicutils.algorithms import * 3 | import basicutils.CONST as GLOBAL 4 | from typing import * 5 | from basicutils.task import * 6 | 7 | def BaiduTTS(text: str) -> str: 8 | """拿百度TTS的链接""" 9 | return f'https://fanyi.baidu.com/gettts?lan=zh&spd=5&source=web&text={text}' 10 | 11 | # def generateTmpFile(b: bytes, fm='png') -> str: 12 | # """生成一个30s后会删掉的临时文件""" 13 | # fn = generateTmpFileName(ext=f'.{fm}') 14 | # with open(fn, 'wb') as f: 15 | # f.write(b) 16 | # asyncio.ensure_future(rmTmpFile(fn)) 17 | # return fn 18 | 19 | 20 | def generateTmpFileName(pref='', ext='.png', **kwargs): 21 | """生成一个临时文件名""" 22 | return f'''tmp{pref}{randstr(GLOBAL.randomStrLength)}{ext}''' 23 | 24 | 25 | def getFileBytes(s): 26 | if isinstance(s, bytes): 27 | return s 28 | elif s[:4] == 'http': 29 | ret = requests.get(s).content 30 | print(len(ret)) 31 | return ret 32 | else: 33 | with open(s, 'rb') as f: 34 | return f.read() 35 | 36 | import requests 37 | def convert_to_amr(typ: str, lnk: Union[bytes, str], mode: int=0): 38 | if isinstance(lnk, str): 39 | ret = requests.post( 40 | internal_api(f'/convert/amr?format={typ}&mode={mode}'), 41 | data={'lnk': lnk} 42 | ).json()['url'] 43 | else: 44 | ret = requests.post( 45 | internal_api(f'/convert/amr?format={typ}&mode={mode}'), 46 | files={'f': BytesIO(lnk)} 47 | ).json()['url'] 48 | return server_api('/worker/oss/'+ret) 49 | 50 | def convert_file_to_amr(typ: str, fp, mode: int=0): 51 | ret = requests.post( 52 | internal_api(f'/convert/amr?format={typ}&mode={mode}'), 53 | files={'f': open(fp,'rb')} 54 | ).json()['url'] 55 | return server_api('/worker/oss/'+ret) 56 | import base64 57 | from PIL import Image as PImage 58 | def pimg_base64(img: PImage.Image) -> str: 59 | bio = BytesIO() 60 | img.save(bio, format='PNG') 61 | bio.seek(0) 62 | return base64.b64encode(bio.read()).decode('utf-8') -------------------------------------------------------------------------------- /basicutils/network.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel # 为了用json 2 | import json 3 | from basicutils.chain import MessageChain 4 | 5 | from loguru import logger 6 | 7 | # Core内部传输用 8 | class CoreEntity(BaseModel): 9 | """irori系统内部的消息传输形式""" 10 | chain: MessageChain 11 | # player: str = '' # 发送来源player ObjectId 12 | pid: str = '' # 发送来源player id 13 | source: str = '' # 发送来源Sessionid或是相关jwt 14 | meta: dict = {} # 额外参数,对worker会使用ts时间戳来维护忙状态,解析的--参数也会放在这里 15 | jwt: str = '' # 令牌 16 | member: str = '' # 实际发送者的player号 17 | @classmethod 18 | def handle_json(cls, j): 19 | d = json.loads(j) 20 | d['chain'] = MessageChain.auto_make(d['chain']) 21 | return cls(**d) 22 | @classmethod 23 | def wrap_rawstring(cls, msg: str): 24 | mt = {'msg': msg} 25 | return cls( 26 | chain=MessageChain.get_empty(), 27 | meta=mt 28 | ) 29 | @classmethod 30 | def wrap_strchain(cls, msg: str): 31 | return cls( 32 | chain=MessageChain.auto_make(msg), 33 | ) 34 | def unpack_rawstring(self) -> str: 35 | return self.meta.get('msg', '') 36 | @classmethod 37 | def wrap_dict(cls, d: dict): 38 | return cls( 39 | chain=MessageChain.get_empty(), 40 | meta=d 41 | ) 42 | # 更pydantic2.0后,MessageChain不能直接输出子元素的field,所以这里要手动处理 43 | def old_style_json(self): 44 | j = self.model_dump() 45 | j['chain'] = self.chain.to_str_list() 46 | return json.dumps(j) -------------------------------------------------------------------------------- /basicutils/quic.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | import asyncio 3 | from QUICFaker import Configuration as cfg 4 | from loguru import logger 5 | from collections import defaultdict 6 | from basicutils.network import * 7 | # from enum import Enum 8 | 9 | class QUICSessionDefs: 10 | # 心跳包字符 11 | hbyte = b'D' 12 | # meta键 13 | META_CHANNEL_NAME = 'C' 14 | META_SENDTO_TARGET = 'S' 15 | # channel常量 16 | CHANNEL_COMMON = 'CMN' 17 | CHANNEL_TASK = 'TSK' 18 | CHANNEL_CONTROL = 'CTL' 19 | 20 | 21 | class QUICSessionBase(ABC): 22 | def __init__(self, reader: asyncio.StreamReader, writer: asyncio.StreamWriter): 23 | self._reader = reader 24 | self._writer = writer 25 | self._ato = 0 26 | self._contentbuffer = [] 27 | self._alive = True 28 | self._Qchannel = defaultdict(asyncio.Queue) 29 | self.connect_future = asyncio.ensure_future(self.keep_connect()) 30 | self._readstate = -1 31 | self.initialize() 32 | 33 | @abstractmethod 34 | def initialize(self): 35 | raise NotImplementedError 36 | 37 | # @abstractmethod 38 | # def _distro_msg(self, msg: bytes): 39 | # raise NotImplementedError 40 | 41 | # @abstractmethod 42 | async def recv(self, channel=QUICSessionDefs.CHANNEL_COMMON) -> CoreEntity: 43 | dt = await self._Qchannel[channel].get() 44 | # logger.info(dt) 45 | return dt 46 | # raise NotImplementedError 47 | @logger.catch 48 | async def keep_connect(self): 49 | while 1: 50 | res = await self._reader.read(cfg.buffer) 51 | logger.debug(">>> {}", res) 52 | if not res: 53 | for k, v in self._Qchannel.items(): 54 | v.put_nowait(ConnectionResetError('Connection closed')) 55 | raise ConnectionResetError("连接已断开") # 考虑用callback解决 56 | if res == QUICSessionDefs.hbyte: # 心跳包字串 57 | logger.debug('Heartbeat') 58 | else: 59 | for bts in res: 60 | 61 | if self._readstate == -1: # 期待数字 62 | if self._ato < 0: 63 | self._ato = 0 64 | if bts in range(48, 57+1): 65 | self._ato = self._ato * 10 + bts - 48 66 | else: 67 | self._contentbuffer.append(bytes([bts])) 68 | self._ato -= 1 69 | self._readstate = 0 # 期待payload 70 | else: 71 | if self._ato>0: 72 | self._contentbuffer.append(bytes([bts])) 73 | self._ato -= 1 74 | 75 | if self._ato == 0: 76 | self._readstate = -1 77 | byte_stream = b''.join(self._contentbuffer) 78 | self._contentbuffer = [] 79 | logger.debug(byte_stream) 80 | ent = CoreEntity.handle_json(byte_stream) 81 | channel = ent.meta.get(QUICSessionDefs.META_CHANNEL_NAME, QUICSessionDefs.CHANNEL_COMMON) 82 | 83 | logger.debug('put to {}', channel) 84 | self._Qchannel[channel].put_nowait(ent) 85 | # logger.debug("ato: {}, bts: {}", self._ato, bts) 86 | logger.debug("ato: {}", self._ato) 87 | # logger.debug("buf: {}", self._contentbuffer) 88 | 89 | async def send(self, ent: CoreEntity) -> NoReturn: 90 | """发送CoreEntity对象""" 91 | data = ent.json() 92 | logger.debug("<<< {}", data) 93 | payload = data.encode('utf-8') 94 | contentlen = bytes(str(len(payload)), 'utf-8') 95 | self._writer.write(contentlen + payload) 96 | 97 | -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | #idea 10 | .idea/ 11 | 12 | #visual studio code 13 | .vscode/ 14 | .vscode/* 15 | !.vscode/settings.json 16 | !.vscode/tasks.json 17 | !.vscode/launch.json 18 | !.vscode/extensions.json 19 | 20 | # Distribution / packaging 21 | .Python 22 | build/ 23 | develop-eggs/ 24 | dist/ 25 | downloads/ 26 | eggs/ 27 | .eggs/ 28 | lib/ 29 | lib64/ 30 | parts/ 31 | sdist/ 32 | var/ 33 | wheels/ 34 | *.egg-info/ 35 | .installed.cfg 36 | *.egg 37 | MANIFEST 38 | 39 | # PyInstaller 40 | # Usually these files are written by a python script from a template 41 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 42 | *.manifest 43 | *.spec 44 | 45 | # Installer logs 46 | pip-log.txt 47 | pip-delete-this-directory.txt 48 | 49 | # Unit test / coverage reports 50 | htmlcov/ 51 | .tox/ 52 | .coverage 53 | .coverage.* 54 | .cache 55 | nosetests.xml 56 | coverage.xml 57 | *.cover 58 | .hypothesis/ 59 | .pytest_cache/ 60 | 61 | # Translations 62 | *.mo 63 | *.pot 64 | 65 | # Django stuff: 66 | *.log 67 | local_settings.py 68 | db.sqlite3 69 | 70 | # Flask stuff: 71 | instance/ 72 | .webassets-cache 73 | 74 | # Scrapy stuff: 75 | .scrapy 76 | 77 | # Sphinx documentation 78 | docs/_build/ 79 | 80 | # PyBuilder 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # celery beat schedule file 90 | celerybeat-schedule 91 | 92 | # SageMath parsed files 93 | *.sage.py 94 | 95 | # Environments 96 | .env 97 | .venv 98 | env/ 99 | venv/ 100 | ENV/ 101 | env.bak/ 102 | venv.bak/ 103 | 104 | # Spyder project settings 105 | .spyderproject 106 | .spyproject 107 | 108 | # Rope project settings 109 | .ropeproject 110 | 111 | # mkdocs documentation 112 | /site 113 | 114 | # mypy 115 | .mypy_cache/ 116 | -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.0" 4 | - "3.1" 5 | - "3.2" 6 | - "3.3" 7 | - "3.4" 8 | - "3.5" 9 | - "3.6" 10 | - "3.7" 11 | # command to install dependencies 12 | install: 13 | - pip install -r requirements.txt 14 | # command to run tests 15 | script: 16 | - python -m unittest 17 | -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 kumbong hermann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

6 | Quine McCluskey Circuit Minimizer. 7 | 8 |

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |

18 | 19 | A robust :hammer:, insanely fast :zap: and stupidly easy to use :sleeping: CLI tool for minimizing sum of products via the Quine Mccluskey minimization technique. 20 | 21 | ## Features :gem: 22 | * Written in uncomplicated python :innocent: 23 | * Supports don't cares and variables for representing expression 24 | * Display of all steps leading to solution :droplet: 25 | * Works on Mac, Linux and Windows 26 | * Provides all other alternative solutions to the minimization problem 27 | 28 | ## Installation :package: 29 | 1. Clone the repo 30 | ```bash 31 | git clone https://github.com/Kumbong/quine_mccluskey.git 32 | cd quine_mccluskey 33 | ``` 34 | 2. Install dependencies 35 | ```bash 36 | pip install -r requirements.txt 37 | ``` 38 | 39 | ## Usage :computer: 40 | You can use the following optional arguments according to your needs: 41 | 42 | * `-d`, `--dcares` (list of numbers): **list of dont't cares (default=[ ])** 43 | * `-v`, `--variables` (list of chars): **list of characters for representing result (default = [ ])** 44 | * `-s`, `--show_steps` (yes|no): **show steps leading to solution (default=yes)** 45 | * `-p`, `--show_step` (sum of products): **sum of products are used to be reduce the function** 46 | * `-m`, `--show_step` (minterms): **sum of minterms are used to be reduce the function** 47 | 48 | Example 49 | ```bash 50 | python -m qmccluskey -m 0,1,3,7,8,9,11,15 -d 12 -v a,b,c,d 51 | python -m qmccluskey -p a'b'c'+b'cd'+a'bcd'+ab'c' -v a,b,c,d 52 | ``` 53 | 54 | ## Demo :movie_camera: 55 | Solution for the the Example above 56 | 57 | ![](assets/images/grouping.png) 58 | ![](assets/images/combining.png) 59 | ![](assets/images/coverage.png) 60 | ![](assets/images/solution.png) 61 | 62 | ## Contributing :gift: [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dwyl/esta/issues) 63 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 64 | Please make sure to update tests as appropriate. 65 | 66 | ## Want to talk more?? 67 | If you are interested in helping or have something to suggest or just want to chat with me, you can reach me through the following media . 68 | * Email - kumbonghermann@gmail.com 69 | * Follow me on ![twitter]("https://twitter.com/KumbongHermann") 70 | * Let's connect on ![linkedin]("https://www.linkedin.com/in/kumbong-hermann-406481110/") 71 | * I'm on ![hackererrank]("https://www.hackerrank.com/kumbonghermann") 72 | 73 | ## References :book: 74 | * Petrick's method 75 | https://en.wikipedia.org/wiki/Petrick%27s_method 76 | * quine mccluskey also in python 77 | https://github.com/tpircher/quine-mccluskey 78 | * readme.md 79 | https://github.com/karan/joe 80 | 81 | ## Todos :pencil: 82 | - Automate build 83 | - Improve code quality to A+ 84 | - Complete GUI module 85 | - Write tests 86 | - Include steps for petrick's method 87 | 88 | 89 | License :key: 90 | ---- 91 | 92 | MIT © Kumbong Hermann 93 | 94 | -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/assets/images/circuit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/quine_mccluskey/assets/images/circuit.jpg -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/assets/images/combining.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/quine_mccluskey/assets/images/combining.png -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/assets/images/coverage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/quine_mccluskey/assets/images/coverage.png -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/assets/images/grouping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/quine_mccluskey/assets/images/grouping.png -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/assets/images/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/quine_mccluskey/assets/images/solution.png -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/assets/images/win_icon.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/quine_mccluskey/assets/images/win_icon.jpeg -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/quine_mccluskey/core/__init__.py -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/core/qm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/quine_mccluskey/core/qm/__init__.py -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/core/qm/petrick.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | #read more about petrick's method here https://en.wikipedia.org/wiki/Petrick%27s_method 4 | def multiply(t1,t2): 5 | """ 6 | Multiplies (expands) two binary expressions t1 and t2 based on the distributive rule 7 | 8 | Args: 9 | t1 (str): first binary expression 10 | t2 (str): second binary expression 11 | 12 | Returns: 13 | A string representing the expansion of the boolean algebraic expressions 14 | 15 | 16 | """ 17 | t1 = t1.split('+') 18 | t2 = t2.split('+') 19 | 20 | prod = '' 21 | for m in t1: 22 | temp = "" 23 | for n in t2: 24 | 25 | if t1.index(m) == len(t1)-1 and t2.index(n) == len(t2)-1: 26 | if m!=n: 27 | temp=(temp+m+n) 28 | else: 29 | temp += m 30 | 31 | else: 32 | if m!=n: 33 | temp=temp + m+n+'+' 34 | else: 35 | temp+=m+'+' 36 | 37 | prod+=temp 38 | 39 | return prod 40 | 41 | 42 | 43 | def remove_dups(expr): 44 | """ 45 | Removes duplicates from an expression 46 | 47 | Args: 48 | expr (str): Expression to be simplified 49 | 50 | Returns: 51 | A string representing the simplified version of the expression 52 | 53 | """ 54 | #removes duplicates from the expression both repeating characters and 55 | #repeating terms 56 | 57 | #removes any duplicate terms in the expression 58 | expr = expr.split('+') 59 | 60 | #now remove any duplicate characters in the expression 61 | temp_expr = [] 62 | for term in expr: 63 | # print(term) 64 | #allow the characters to appear in a specific order e.g abcd to allow easy comparison 65 | temp_term = list(term) 66 | temp_term.sort() 67 | 68 | term = '' 69 | for char in temp_term: 70 | term+= char 71 | 72 | new_term ="" 73 | for char in term: 74 | if char not in new_term: 75 | new_term+=char 76 | temp_expr.append(new_term) 77 | 78 | #remove any repeating terms from the expression 79 | temp_expr = list(set(temp_expr)) 80 | return temp_expr 81 | 82 | def multiply_all(terms): 83 | 84 | prod = terms[0] 85 | for i in range(1,len(terms)): 86 | # print('terms [i] ',terms[i]) 87 | # print('prod', prod) 88 | prod = (multiply(terms[i],prod)) 89 | # print(prod) 90 | return prod 91 | 92 | def reduce_expr(expr): 93 | """ 94 | Reduces a boolean algebraic expression based on the identity X + XY = X 95 | 96 | Args: 97 | expr (str): representation of the boolean algebraic expression 98 | 99 | Returns: 100 | A string representing the reduced algebraic expression 101 | 102 | """ 103 | reduced = True 104 | for term in expr: 105 | matches = [t for t in expr if t!=term and len(set(term).intersection(set(t))) == len(term)] 106 | if(matches): 107 | reduced = False 108 | 109 | if reduced: 110 | return expr 111 | 112 | new_expr = [] 113 | temp_expr = expr 114 | for term in expr: 115 | #find the term that differs with it by at most one position 116 | matches = [t for t in expr if t!=term and len(set(term).intersection(set(t))) == len(term)] 117 | if(matches): 118 | new_expr.append(term) 119 | temp_expr.remove(term) 120 | for match in matches: 121 | temp_expr.remove(match) 122 | #if such a term is found reduce it by the rule x+ xy =x 123 | #remove both terms from the list and add to new expression 124 | #if no term is found add the term to the next expression 125 | expr = reduce_expr(new_expr+temp_expr) 126 | 127 | return expr 128 | 129 | def min_len_terms(terms): 130 | """ 131 | Finds the shortest term 132 | 133 | Args: 134 | terms: A list of terms 135 | 136 | Returns: 137 | The terms that have the minimum length 138 | 139 | """ 140 | 141 | minlen = len(min(terms,key=lambda x: len(x))) 142 | 143 | terms = [term for term in terms if len(term) == minlen] 144 | 145 | return terms 146 | 147 | def count_literals(term): 148 | """ 149 | Counts the number of literals in a term 150 | 151 | Args: 152 | term : A string containing literals 153 | 154 | 155 | Returns: 156 | The number of literals in term 157 | """ 158 | 159 | count = 0 160 | for char in term: 161 | if char != "_": 162 | count+=1 163 | 164 | return count 165 | 166 | def fewest_literals(terms): 167 | """ 168 | Returns the terms that contain the fewest number of literals 169 | 170 | Args: 171 | terms: A list of of terms 172 | 173 | Returns: 174 | A list of minterms with the fewest number of literals 175 | 176 | """ 177 | 178 | #returns the terms with the fewest literals 179 | min_count = count_literals(min(terms,key=lambda x: count_literals(x))) 180 | 181 | min_lits = [] 182 | 183 | for term in terms: 184 | if count_literals(term) == min_count: 185 | min_lits.append(term) 186 | 187 | return min_lits 188 | 189 | -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/core/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/quine_mccluskey/core/tests/__init__.py -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/core/tests/test_petrick.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, main 2 | from core.qm.petrick import * 3 | 4 | class TestPetrick(TestCase): 5 | def setup(self): 6 | pass 7 | 8 | def test_multiply(self): 9 | self.fail() 10 | 11 | def test_remove_dups(self): 12 | self.fail() 13 | 14 | def test_multiply_all(self): 15 | self.fail() 16 | 17 | def test_reduce_expr(self): 18 | self.fail() 19 | 20 | def test_min_len_terms(self): 21 | self.fail() 22 | 23 | def test_count_literal(self): 24 | self.fail() 25 | 26 | def test_fewest_literals(self): 27 | self.fail() -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/core/tests/test_qm.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, main 2 | from core.qm.qm import QM 3 | 4 | class TestQM(TestCase): 5 | def setup(self): 6 | pass 7 | 8 | def test_to_binary(self): 9 | 10 | #test if the strings are the actual binary representations 11 | #for each of the strings 12 | minterms = [1,2,3,4,5,6,15] 13 | qm = QM(minterms) 14 | expected_conversion = ['0001','0010','0011','0100','0101','0110','1111'] 15 | conversion = qm.to_binary(minterms) 16 | self.assertEqual(conversion,expected_conversion) 17 | 18 | #check if the number of bits in each binary string is the same for 19 | #all 20 | for term in conversion: 21 | self.assertEqual(len(term),4) 22 | 23 | #check if the same applies to the don't cares 24 | 25 | def test_combine(self): 26 | #test for two terms that are not supposed to be combined 27 | #expected return value should be None 28 | minterms = [1,2,3,4,5,6,15] 29 | qm = QM(minterms) 30 | 31 | self.assertEqual(qm.combine('0000','1001'),None) 32 | #test for values(without _ ) that differ by exactly one position 33 | #expected return value should have a new value with a _ in the positiion of difference 34 | 35 | self.assertEqual(qm.combine('0000','0001'),'000_') 36 | #test for values(without _ ) that differ by exactly one position 37 | #expected return value should have a new value with a _ in the positiion of difference 38 | 39 | self.assertEqual(qm.combine('000_','100_'),'_00_') 40 | 41 | #test for values that differ in length 42 | #valueerror exception should be thrown 43 | with self.assertRaises(ValueError): 44 | qm.combine('00000','0001'),ValueError 45 | 46 | #test for values that are the same 47 | #None should be returned 48 | self.assertEqual(qm.combine('0000','0000'),None) 49 | 50 | 51 | def test_combine_groups(self): 52 | #test combination for some random groups 53 | 54 | minterms = [1,2,3,4,5,6,15] 55 | qm = QM(minterms) 56 | self.assertEqual(qm.combine_groups(['0001','1000'],['0011','1001','1100']),['00_1','_001','100_','1_00']) 57 | self.assertEqual(qm.combine_groups(['0000'],['0001','1000']),['000_','_000']) 58 | self.assertEqual(qm.combine_groups([],['0001','1000']),[]) 59 | self.assertEqual(qm.combine_groups([],[]),[]) 60 | 61 | def test_combine_generation(self): 62 | self.fail() 63 | 64 | def test_group_minterms(self): 65 | self.fail() 66 | 67 | def test_pis(self): 68 | self.fail() 69 | 70 | def test_can_cover(self): 71 | self.fail() 72 | 73 | def test_epis(self): 74 | self.fail() 75 | 76 | def test_other_pis(self): 77 | self.fail() 78 | -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/qmccluskey.py: -------------------------------------------------------------------------------- 1 | import sys 2 | #sys.path.append("F:/py/SelfMadeMiraiPythonScript/quine_mccluskey") 3 | sys.path.append('./quine_mccluskey') 4 | from colorclass import Windows 5 | from .core.qm.qm import QM 6 | # TODO 7 | # add validation for variables from CLI and GUI 8 | 9 | # enable colours on terminal for windows 10 | 11 | if sys.platform == "win32": 12 | Windows.enable(auto_colors=True) 13 | 14 | 15 | # used to check if a string can be an integer 16 | def representsInt(s): 17 | # checks if the string s represents an integer 18 | try: 19 | int(s) 20 | return True 21 | except ValueError: 22 | return False 23 | 24 | def maid(argssop='',argsdont_cares='',argsvariables='',minterms=[]): 25 | 26 | if argssop: 27 | sop = argssop.split('+') 28 | 29 | varSet = set() 30 | for _ in sop: 31 | for __ in _: 32 | if __ != "'":varSet.add(__) 33 | argsvariables = ','.join(varSet) 34 | 35 | variables = argsvariables.split(',') 36 | 37 | number_list = {} 38 | for num in range(len(variables) ** 2 - 1): 39 | bin_num = str(bin(num)[2:]) 40 | while len(bin_num) < len(variables): 41 | bin_num = '0' + bin_num 42 | number_list[bin_num] = num 43 | 44 | dic = {} 45 | list_dic = [] 46 | for product in sop: 47 | 48 | for variable in product: 49 | 50 | if ord(variable) in range(65, 123) and product.index(variable) + 1 < len(product) and product[ 51 | product.index(variable) + 1] == "'": 52 | dic[variable] = 0 53 | 54 | elif ord(variable) in range(65, 123): 55 | dic[variable] = 1 56 | 57 | list_dic.append(dic) 58 | dic = {} 59 | result = [] 60 | for mt in list_dic: 61 | for num in number_list: 62 | count = 0 63 | for i in range(len(variables)): 64 | if variables[i] in mt and int(num[i]) == mt[variables[i]]: 65 | count += 1 66 | if len(mt) == count: 67 | result.append(number_list[num]) 68 | minterms = list(set(result)) 69 | 70 | # make sure all the values in the values entered for minterms are valid integers 71 | if not minterms: 72 | return 'Error: sum of product values expected for minterms' 73 | 74 | for mt in minterms: 75 | # if it is not a whitespace and it is not an integer 76 | if (mt and not representsInt(mt)) or ((mt and representsInt(mt)) and int(mt) < 0): 77 | return 'Error: Integer values expected for minterms' 78 | 79 | # make sure all the values in the values entered for dont cares are valid integers 80 | if argsdont_cares: 81 | dcares = argsdont_cares.split(',') 82 | # make sure the don't cares are all integer values 83 | for dc in dcares: 84 | if (dc and not representsInt(dc)) or ((dc and representsInt(dc)) and int(dc) < 0): 85 | return 'Error : Integer values expected for don\'t cares' 86 | 87 | # a term cannot be a don't care and a minterm at the same time 88 | if dc in minterms: 89 | return 'Error: A term cannot be a minterm and a don\'t care at the same time' 90 | 91 | else: 92 | dcares = [] 93 | 94 | ##################################add validation for variables here #################### 95 | if argsvariables: 96 | variables = argsvariables.split(',') 97 | 98 | # filter out duplicates in the variables entered 99 | 100 | # if there were duplicate terms then let the user know 101 | if len(variables) != len(list(set(variables))): 102 | return "Error: Duplicate terms not allowed for variables" 103 | # make sure the variables entered are enough to represent the expression 104 | # else raise a value error exception and close the program 105 | 106 | # check the number of variables needed 107 | # take into consideration the minter's as well 108 | 109 | mterms = map(lambda x: int(x), minterms) 110 | dcs = map(lambda x: int(x), dcares) 111 | max_minterm = max(list(mterms) + list(dcs)) 112 | 113 | max_minterm = bin(max_minterm)[2:] 114 | 115 | if len(variables) != len(max_minterm): 116 | return "Error: Number of variables entered is not enough to represent expression" 117 | else: 118 | variables = [] 119 | 120 | qm = QM(minterms, dcares, variables) 121 | sols = qm.minimize() 122 | outputStr='\n'.join(sols) 123 | 124 | return outputStr 125 | -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/requirements.txt: -------------------------------------------------------------------------------- 1 | terminaltables==3.1.0 2 | colorclass==2.2.0 3 | -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup(name='Distutils', 4 | version='1.0', 5 | license = "MIT License", 6 | description='Python implementation of quine mccluskey algorithm', 7 | author='Kumbong Hermann', 8 | author_email='kumbonghermann@gmail.com', 9 | url='', 10 | packages=['gui','qm' ,'tests'], 11 | long_description=open('README.md').read(), 12 | ) 13 | 14 | -------------------------------------------------------------------------------- /basicutils/quine_mccluskey/usample.txt: -------------------------------------------------------------------------------- 1 | python -m qmccluskey -m 0,1,3,7,8,9,11,15 -d 12 -v a,b,c,d 2 | python -m qmccluskey -p b'd+a'bc'+a'bcd' -v a,b,c,d -------------------------------------------------------------------------------- /basicutils/rpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/basicutils/rpc/__init__.py -------------------------------------------------------------------------------- /basicutils/rpc/openai.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import random 3 | import json 4 | import http 5 | from typing import List 6 | import urllib 7 | import requests 8 | from basicutils.rpc.translate import Baidu 9 | from loguru import logger 10 | 11 | class OpenAI: 12 | api_key = None 13 | 14 | @classmethod 15 | def get_cred(cls): 16 | if cls.api_key is None: 17 | from fapi.models.Auth import IroriConfig 18 | cfg = IroriConfig.objects().first() 19 | cls.api_key = cfg.api_keys['openaikey'] 20 | return cls.api_key 21 | 22 | @classmethod 23 | def chat(cls, prompt: str, translate=False) -> List[str]: 24 | k = cls.get_cred() 25 | if translate: 26 | prompt = Baidu.trans('zh', 'en', prompt) 27 | logger.debug(f'translated: {prompt}') 28 | r = requests.post( 29 | "https://api.openai.com/v1/chat/completions", 30 | headers={ 31 | "Content-Type": "application/json", 32 | "Authorization": "Bearer " + k 33 | }, 34 | json={ 35 | # "model": "text-davinci-003", 36 | "model": "gpt-3.5-turbo", 37 | # "model": "gpt-4", 38 | "messages": [{"role": "user", "content": prompt}], 39 | "temperature": 0.6, 40 | "max_tokens": 3600 41 | }, 42 | # verify=False 43 | ) 44 | choices = [] 45 | logger.debug(r.text) 46 | for i in r.json()['choices']: 47 | choices.append(i['message']['content'].strip()) 48 | if translate: 49 | logger.debug(f'before translation: {choices[-1]}') 50 | choices[-1] = Baidu.trans('en', 'zh', choices[-1]) 51 | return choices 52 | 53 | 54 | -------------------------------------------------------------------------------- /basicutils/rpc/translate.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import random 3 | import json 4 | import http 5 | import urllib 6 | import requests 7 | import re 8 | import time 9 | from loguru import logger 10 | 11 | 12 | # 网页白嫖 13 | class Bing: 14 | timestamp_key = None 15 | token = None 16 | valid_time = None 17 | session = requests.session() 18 | session.headers = { 19 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36', 20 | 'accept-encoding': 'gzip, deflate, br', 21 | } 22 | translator_url = 'https://www.bing.com/translator' 23 | translate_url = 'https://www.bing.com/ttranslatev3?isVertical=1&&IG=C14796C62F544E239E123D9292F50339&IID=translator.5026' 24 | cred_pat = re.compile(r"""var params_AbusePreventionHelper = \[(\d+),"([\w\-]+)",(\d+)\];""") 25 | @classmethod 26 | def get_cred(cls): 27 | if cls.timestamp_key is None or cls.timestamp_key + cls.valid_time < int(time.time() * 1000): 28 | r = cls.session.get(cls.translator_url) 29 | timestamp_key, cls.token, valid_time = cls.cred_pat.search(r.text).groups() 30 | cls.timestamp_key = int(timestamp_key) 31 | cls.valid_time = int(valid_time) 32 | return cls.timestamp_key, cls.token 33 | 34 | @classmethod 35 | def trans(cls, _from, _to, _text): 36 | # zh-Hans 37 | # en 38 | cls.get_cred() 39 | resp = cls.session.post(cls.translate_url, data={ 40 | 'fromLang': _from, 41 | 'to': _to, 42 | 'key': cls.timestamp_key, 43 | 'token': cls.token, 44 | 'text': _text, 45 | }) 46 | logger.debug({ 47 | 'fromLang': _from, 48 | 'to': _to, 49 | 'key': cls.timestamp_key, 50 | 'token': cls.token, 51 | 'text': _text, 52 | }) 53 | # logger.debug(resp) 54 | # logger.debug(resp.request.headers) 55 | # logger.debug(resp.headers) 56 | # logger.debug(resp.content) 57 | # logger.debug(resp.text) 58 | return ''.join(map(lambda x: x['text'], resp.json()[0]['translations'])) 59 | 60 | 61 | # api 62 | class Baidu: 63 | appid = None 64 | secret = None 65 | 66 | @classmethod 67 | def get_cred(cls): 68 | if cls.appid is None or cls.secret is None: 69 | from fapi.models.Auth import IroriConfig 70 | cfg = IroriConfig.objects().first() 71 | cls.appid, cls.secret = cfg.api_keys['baidu.fanyi.appid'], cfg.api_keys['baidu.fanyi.secret'] 72 | return cls.appid, cls.secret 73 | 74 | @classmethod 75 | def trans(cls, _from, _to, _text): 76 | conn = None 77 | myurl = '/api/trans/vip/translate' 78 | salt = random.randint(32768, 65536) 79 | 80 | baidu_appid, baidu_secretKey = cls.get_cred() 81 | 82 | sign = baidu_appid + _text + str(salt) + baidu_secretKey 83 | sign = hashlib.md5(sign.encode()).hexdigest() 84 | myurl = myurl + '?appid=' + baidu_appid + '&q=' + urllib.parse.quote( 85 | _text) + '&from=' + _from + '&to=' + _to + '&salt=' + str(salt) + '&sign=' + sign 86 | 87 | try: 88 | conn = http.client.HTTPConnection('api.fanyi.baidu.com') 89 | conn.request('GET', myurl) 90 | response = conn.getresponse() 91 | result_all = response.read().decode("utf-8") 92 | result = json.loads(result_all) 93 | li = [] 94 | for r in result['trans_result']: 95 | li.append(r['dst']) 96 | res = '\n'.join(li) 97 | 98 | except: 99 | res = f"网络连接错误" 100 | 101 | finally: 102 | if conn: 103 | conn.close() 104 | return res -------------------------------------------------------------------------------- /basicutils/task.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | import sys 3 | import argparse 4 | from typing import Union 5 | from loguru import logger 6 | 7 | class ArgumentParser(argparse.ArgumentParser): 8 | def _get_action_from_name(self, name): 9 | """Given a name, get the Action instance registered with this parser. 10 | If only it were made available in the ArgumentError object. It is 11 | passed as it's first arg... 12 | """ 13 | container = self._actions 14 | if name is None: 15 | return None 16 | for action in container: 17 | if '/'.join(action.option_strings) == name: 18 | return action 19 | elif action.metavar == name: 20 | return action 21 | elif action.dest == name: 22 | return action 23 | def error(self, message): 24 | raise argparse.ArgumentError(None, message) 25 | # exc = sys.exc_info()[1] 26 | # print('====>', exc) 27 | # if exc: 28 | # exc.argument = self._get_action_from_name(exc.argument_name) 29 | # raise exc 30 | # super(ArgumentParser, self).error(message) 31 | try: 32 | from cfg import dist_host, web_port, internal_host 33 | except ImportError: 34 | logger.warning(' or or not found, using http://localhost:1234 instead') 35 | dist_host, web_port, internal_host = 'http://localhost', 1234, 'http://localhost' 36 | 37 | def server_api(relative_path: str) -> str: 38 | return f"{dist_host}:{web_port}{relative_path}" 39 | 40 | def internal_api(relative_path: str) -> str: 41 | return f"{internal_host}:{web_port}{relative_path}" 42 | 43 | 44 | # def convert_to_amrb(typ: str, content: bytes): 45 | # ret = requests.post( 46 | # server_api(f'/convert/amr?format={typ}&mode=0'), 47 | # data={'lnk': lnk} 48 | # ).json()['url'] 49 | # return server_api('/worker/oss/'+ret) -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import pexpect 3 | import pexpect.popen_spawn 4 | import os 5 | 6 | 7 | account = input("QQ号? >>>") 8 | password= input("密码? >>>") 9 | 10 | 11 | desc = """ 12 | 成功进入shell 13 | 手动配置教程: 14 | 1. 进入/env/ 15 | > 如果你没用过linux的话,用"cd /env"(不带引号)来进入这个目录 16 | 2. 根据需要调节/env/plugins/MiraiAPIHTTP/setting.yml 17 | > 如果你没用过linux的话,用"nano /env/plugins/MiraiAPIHTTP/setting.yml"(不带引号)然后通过方向键和键盘编辑这个文本 18 | 3. 主要调节port的值,确保你填的端口没被占用,authKey如果你真的改了这个向导很可能会用不了 19 | 4. 调好后保存文件退出 20 | > 如果你没用过nano的话,CTRL+s保存文件,然后CTRL+x退出 21 | 5. 后台运行"/env/run.sh" 22 | > 如果你不会用后台,"screen -R mirai",然后"/env/run.sh",然后CTRL+a然后敲一下d 23 | 6. 输入exit退回向导,或者如果你愿意的话可以进入/irori折腾( 24 | """ 25 | 26 | cmd = '' 27 | 28 | authkey = '1145141919810' 29 | os.chdir('/env') 30 | mirai_console = pexpect.spawn('bash /env/run.sh') 31 | 32 | mirai_console.sendline(f'login {account} {password}') 33 | 34 | try: 35 | while cmd not in ('DONE', 'BASH'): 36 | try: 37 | mirai_console.sendline(cmd) 38 | while True: 39 | mirai_console.expect('\r\n', timeout=3) 40 | print(mirai_console.before.decode('utf-8')) 41 | except: 42 | cmd = input('上面是mirai_console的输出,\n如果你觉得已经没问题的话请输入"DONE"来启动irori,\n或者输入"BASH"来开启一个shell进行手动配置。除了"BASH"和"DONE"之外的所有输入会被转发至mirai终端>>>') 43 | if cmd == 'BASH': 44 | print(desc) 45 | os.system('bash') 46 | t = input('您刚才有修改authKey吗?如果有请现在输入新的authKey,否则留空>>>') 47 | if t: authkey = t 48 | except KeyboardInterrupt: 49 | print('irori启动...') 50 | 51 | os.chdir('/irori') 52 | with open('authdata','w') as f: 53 | f.write(f"{account}\n{authkey}\nhttp://127.0.0.1:28080/") 54 | 55 | with open('irori.py','r') as f: exec(f.read()) -------------------------------------------------------------------------------- /database_migrator.py: -------------------------------------------------------------------------------- 1 | from GLOBAL import * 2 | from database_utils import * 3 | 4 | import os 5 | 6 | 7 | for i in os.listdir('credits/'): 8 | print(i) 9 | with open(f"credits/{i}", 'r') as f: 10 | c =int(f.read().strip()) 11 | ent = CreditLog.chk(i) 12 | ent.credit = c 13 | ent.save() -------------------------------------------------------------------------------- /docker_init.sh: -------------------------------------------------------------------------------- 1 | docker build -t irori:latest . -------------------------------------------------------------------------------- /fapi/G.py: -------------------------------------------------------------------------------- 1 | # adapters = {} 2 | # initialized = False -------------------------------------------------------------------------------- /fapi/MiraiSession.py: -------------------------------------------------------------------------------- 1 | from fapi.Sessions import * 2 | from fapi.utils.jwt import * 3 | import json 4 | from Worker import task 5 | from loguru import logger 6 | 7 | class MiraiSession(Session): 8 | # def __init__(self, adapter_id: Union[str, Adapter]): 9 | # self._alive = True 10 | # self._ases = aiohttp.ClientSession() 11 | # self.aid = adapter_id 12 | # self.syncid = adapter_id 13 | # self.jwt = generate_jwt(adapter_id) 14 | # self.dbobj = Adapter.trychk(self.aid) 15 | 16 | async def _receive_loop(self, wsurl: str): 17 | """ 18 | 仅用于将消息从mirai拉下来执行处理,不用于回传消息。 19 | 只要接收回环还在运行,就认为会话存活。 20 | 反之销毁会话必须停止接收回环。 21 | """ 22 | async for msg in self.ws: 23 | # if not self._alive: 24 | # logger.warning('manually closed') 25 | # break 26 | try: 27 | if msg.type == aiohttp.WSMsgType.TEXT: 28 | logger.debug(msg.data) 29 | j = json.loads(msg.data) 30 | # logger.warning(j) 31 | if 'data' in j and 'type' in j['data']: 32 | if j['data']['type'] == 'GroupMessage': 33 | pid = str(j['data']['sender']['group']['id'] + (1<<39)) 34 | ent = CoreEntity( 35 | jwt=generate_session_jwt(self.sid), 36 | pid=pid, 37 | source=self.sid, 38 | member=str(j['data']['sender']['id']), 39 | meta={}, 40 | chain=MessageChain.auto_make(j['data']['messageChain']) 41 | ) 42 | 43 | elif j['data']['type'] == 'FriendMessage': 44 | pid = str(j['data']['sender']['id']) 45 | ent = CoreEntity( 46 | jwt=generate_session_jwt(self.sid), 47 | pid=pid, 48 | source=self.sid, 49 | member=str(j['data']['sender']['id']), 50 | meta={}, 51 | chain=MessageChain.auto_make(j['data']['messageChain']) 52 | ) 53 | else: 54 | logger.warning('unsupported message type: {}', j['data']['type']) 55 | continue 56 | # continue # debug 57 | # TODO: 临时消息 58 | if await self._handle_syscall(ent): 59 | continue 60 | try: 61 | # logger.warning(f'conn2wk{ent}') 62 | task.delay(ent.json()) # 向Worker发布任务 63 | except: 64 | logger.critical(traceback.format_exc()) 65 | 66 | else: 67 | logger.critical(msg.type) 68 | logger.critical(f'connection closed {wsurl}') 69 | # elif msg.type == aiohttp.WSMsgType.ERROR: 70 | break 71 | except: 72 | logger.critical(traceback.format_exc()) 73 | self._alive = False 74 | 75 | async def enter_loop(self, wsurl: str): 76 | self._ases = aiohttp.ClientSession() 77 | 78 | """回环入口,对于mirai的对接,向提供的wsurl发起websocket连接""" 79 | self.ws = await self._ases.ws_connect(wsurl, headers={}) 80 | await self.ws.receive_json() # 捕获连接消息 81 | myplayers = [] 82 | await self.ws.send_json({ 83 | "syncId": -1, 84 | "command": "friendList" 85 | }) 86 | ret=(await self.ws.receive_json()) 87 | logger.debug(ret) 88 | for i in ret['data']['data']: 89 | pid = i['id'] 90 | myplayers.append(str(pid)) 91 | 92 | await self.ws.send_json({ 93 | "syncId": -1, 94 | "command": "groupList" 95 | }) 96 | ret=(await self.ws.receive_json()) 97 | logger.debug(ret) 98 | for i in ret['data']['data']: 99 | pid = i['id'] + (1 << 39) 100 | myplayers.append(str(pid)) 101 | 102 | self.receiver = asyncio.ensure_future(self._receive_loop(wsurl)) 103 | return myplayers 104 | 105 | async def close(self): 106 | if self._alive: 107 | self.receiver.cancel() 108 | await self.ws.close() 109 | await self._ases.close() 110 | self._alive = False 111 | 112 | async def _deliver(self, ent: CoreEntity): 113 | pi = int(ent.pid) 114 | payload = { 115 | "syncId": -1, 116 | "content": { 117 | "messageChain": ent.chain.dict()["__root__"] 118 | }, 119 | } 120 | if pi > (1<<32): 121 | pi -= 1<<39 122 | payload['command'] = "sendGroupMessage" 123 | payload['content']['target'] = pi 124 | logger.warning(json.dumps(payload)) 125 | await self.ws.send_json(payload) 126 | else: 127 | payload['command'] = "sendFriendMessage" 128 | payload['content']['target'] = pi 129 | logger.warning(json.dumps(payload)) 130 | await self.ws.send_json(payload) 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /fapi/WebsocketSession.py: -------------------------------------------------------------------------------- 1 | from calendar import c 2 | import starlette 3 | from fapi.Sessions import * 4 | from fapi import * 5 | from Worker import task 6 | from starlette.websockets import WebSocketState 7 | import fastapi 8 | import asyncio 9 | from fapi.utils.jwt import generate_session_jwt 10 | class WebsocketSessionBase(Session): 11 | @abstractmethod 12 | async def _recv_ent(self) -> CoreEntity: 13 | raise NotImplementedError 14 | async def _receive_loop(self): 15 | """仅用于将消息从ws拉下来执行处理,不用于回传消息""" 16 | while self._alive and self.ws.application_state == WebSocketState.CONNECTED: 17 | try: 18 | ent = await self._recv_ent() 19 | if not ent: 20 | continue 21 | if await self._handle_syscall(ent): 22 | continue 23 | try: 24 | task.delay(ent.old_style_json()) # 向Worker发布任务 25 | except: 26 | logger.critical(traceback.format_exc()) 27 | except RuntimeError: 28 | break 29 | except starlette.websockets.WebSocketDisconnect: 30 | break 31 | except: 32 | logger.critical(traceback.format_exc()) 33 | await self.close() 34 | 35 | async def enter_loop(self, ws: fastapi.WebSocket, pid: str): 36 | self.pid = pid 37 | self.ws = ws 38 | await self.ws.accept() 39 | 40 | self.receiver = asyncio.ensure_future(self._receive_loop()) 41 | return [self.pid] 42 | 43 | async def close(self): 44 | await self.ws.close() 45 | await self._ases.close() 46 | self._alive = False 47 | self.receiver.cancel() 48 | 49 | @abstractmethod 50 | async def _deliver(self, ent: CoreEntity): 51 | raise NotImplementedError 52 | 53 | class WebsocketSessionJson(WebsocketSessionBase): 54 | async def _recv_ent(self) -> CoreEntity: 55 | message = await self.ws.receive_json() 56 | j = message['chain'] # chain: 消息链 57 | custom_pid = (message.get('pid') or self.pid) 58 | custom_mid = (message.get('mid') or self.pid) 59 | return CoreEntity( 60 | jwt=generate_session_jwt(self.sid), 61 | pid=custom_pid, 62 | source=self.sid, 63 | member=custom_mid, 64 | meta={}, 65 | chain=MessageChain.auto_make(j) 66 | ) 67 | async def _deliver(self, ent: CoreEntity): 68 | """向ws送ent序列化后的json""" 69 | payload = { 70 | "chain": ent.chain.dict()["__root__"] 71 | } 72 | await self.ws.send_json(payload) 73 | 74 | class WebsocketSessionPlain(WebsocketSessionBase): 75 | async def _recv_ent(self) -> CoreEntity: 76 | return CoreEntity( 77 | jwt=generate_session_jwt(self.sid), 78 | pid=self.pid, 79 | source=self.sid, 80 | member=self.pid, 81 | meta={}, 82 | chain=MessageChain.auto_make(await self.ws.receive_text()) 83 | ) 84 | 85 | async def _deliver(self, ent: CoreEntity): 86 | """向ws送字符串""" 87 | await self.ws.send_text(ent.chain.tostr()) 88 | 89 | class WebsocketSessionOnebot(WebsocketSessionBase): 90 | async def enter_loop(self, ws: fastapi.WebSocket): 91 | self.ws = ws 92 | await self.ws.accept() 93 | await self.ws.send_json({"action": "get_friend_list"}) 94 | while 1: 95 | if self.ws.state == WebSocketState.DISCONNECTED: 96 | return 97 | try: 98 | ret = await self.ws.receive_json() 99 | logger.debug(ret) 100 | myplayers = [] 101 | for i in ret['data']: 102 | pid = i['user_id'] 103 | myplayers.append(str(pid)) 104 | logger.debug("pull private list done") 105 | break 106 | except KeyError: pass 107 | except RuntimeError: break # 断开连接 108 | except: logger.error(traceback.format_exc()) 109 | await self.ws.send_json({"action": "get_group_list"}) 110 | while 1: 111 | if self.ws.state == WebSocketState.DISCONNECTED: 112 | return 113 | try: 114 | ret = await self.ws.receive_json() 115 | logger.debug(ret) 116 | for i in ret['data']: 117 | pid = i['group_id'] 118 | myplayers.append(str(pid + (1 << 39))) 119 | logger.debug("pull group list done") 120 | break 121 | except KeyError: pass # 心跳包 122 | except RuntimeError: break # 断开连接 123 | except: logger.error(traceback.format_exc()) 124 | self.receiver = asyncio.ensure_future(self._receive_loop()) 125 | return myplayers 126 | 127 | 128 | async def _recv_ent(self) -> CoreEntity: 129 | message = await self.ws.receive_json() 130 | if message.get("meta_event_type") == "heartbeat": 131 | return 132 | logger.debug(message) 133 | if message.get("post_type") != "message": 134 | return 135 | 136 | j = message['message'] # chain: 消息链 137 | if group_id := message.get('group_id'): 138 | pid = int(group_id) + (1<<39) 139 | mid = message['user_id'] 140 | else: 141 | pid = message['user_id'] 142 | mid = message['user_id'] 143 | c = MessageChain.auto_make(j) 144 | return CoreEntity( 145 | jwt=generate_session_jwt(self.sid), 146 | pid=str(pid), 147 | source=str(self.sid), 148 | member=str(mid), 149 | meta={}, 150 | chain=c 151 | ) 152 | 153 | async def _deliver(self, ent: CoreEntity): 154 | """向ws送ent序列化后的json""" 155 | pi = int(ent.pid) 156 | if pi & (1<<39): 157 | pi = str(pi - (1<<39)) 158 | payload = { 159 | "action": "send_group_msg", 160 | "params":{ 161 | "group_id": pi, 162 | "message": ent.chain.onebot_sendable() 163 | } 164 | } 165 | else: 166 | payload = { 167 | "action": "send_private_msg", 168 | "params": { 169 | "user_id": pi, 170 | "message": ent.chain.onebot_sendable() 171 | } 172 | } 173 | await self.ws.send_json(payload) 174 | 175 | -------------------------------------------------------------------------------- /fapi/__init__.py: -------------------------------------------------------------------------------- 1 | from fastapi import HTTPException 2 | 3 | 4 | def trueReturn(data=None, msg=""): 5 | return { 6 | 'data': data, 7 | 'msg': msg, 8 | 'status': True 9 | } 10 | 11 | 12 | def falseReturn(code=500, msg="", data=None): 13 | raise HTTPException(code, { 14 | 'data': data, 15 | 'msg': msg, 16 | 'status': False 17 | }) 18 | -------------------------------------------------------------------------------- /fapi/models/Auth.py: -------------------------------------------------------------------------------- 1 | from mongoengine import * 2 | from mongoengine.queryset.base import * 3 | from fapi.models.Base import * 4 | 5 | # from enum import Enum 6 | 7 | # class Scope(Enum): 8 | # """权限集""" 9 | # FILE_MANAGER = "file_manager" 10 | # SYS = "sys" 11 | # OSS_ADMIN = "oss_A" 12 | 13 | # class Role(Base, Document): 14 | # allow = ListField(StringField()) 15 | # name = StringField(primary_key=True) 16 | # @classmethod 17 | # def chk(cls, pk): 18 | # return super().chk(pk) 19 | # @classmethod 20 | # def trychk(cls, pk): 21 | # return super().trychk(pk) 22 | 23 | # class Profile(Base, Document): 24 | # master = ReferenceField("Player") 25 | # name = StringField(primary_key=True) 26 | 27 | import uuid 28 | class IroriUUID(Document): 29 | """单例令牌""" 30 | uuid = StringField() 31 | 32 | @classmethod 33 | def get(cls) -> "IroriUUID": 34 | return cls.objects().first() 35 | 36 | @classmethod 37 | def regen(cls): 38 | cls.objects().modify(uuid=uuid.uuid4()) 39 | 40 | class IroriConfig(Document): 41 | """首选项单例文件,塞数据库为了热更 42 | 放Server端需要读的数据""" 43 | auth_masters = ListField(StringField(), default=[]) # 狗管理名单,可以执行系统调用 44 | 45 | player_whitelist = DictField(default={}) # 白名单,player只能执行给定的指令 {pid: ["#A", "#C"] ...} 46 | player_blacklist = DictField(default={}) # 黑名单,player不能执行指定的指令 {pid: ["#A", "#C"] ...} 47 | player_ignorelist = ListField(default=[]) # 屏蔽player号名单,注意要当成一个set来维护 48 | 49 | startup_connect_actions = ListField(default=[]) # 启动时连接动作,目前只实现了miraiwsurl 50 | 51 | api_keys = DictField(default={}) 52 | """放爬虫需要的api keys,现含的值有: 53 | - baidu.fanyi.appid 54 | - baidu.fanyi.secret 55 | - saucenao.key 56 | """ 57 | @classmethod 58 | def get(cls) -> "IroriConfig": 59 | return cls.objects().first() 60 | 61 | # class Adapter(Base, Document): 62 | # username = StringField(primary_key=True) 63 | # password = StringField() 64 | # role = ReferenceField(Role, reverse_delete_rule=DO_NOTHING) 65 | # items = DictField() 66 | # tokens = ListField(StringField()) 67 | # profile = ReferenceField(Profile) 68 | # @classmethod 69 | # def chk(cls, pk): 70 | # return super().chk(pk) 71 | # @classmethod 72 | # def trychk(cls, pk): 73 | # return super().trychk(pk) 74 | # # def __int__(self): 75 | # # return int(self.username) 76 | # def __str__(self): 77 | # return str(self.username) -------------------------------------------------------------------------------- /fapi/models/Base.py: -------------------------------------------------------------------------------- 1 | from mongoengine import * 2 | from typing import Optional, TypeVar, Union, get_type_hints 3 | import datetime 4 | from mongoengine.fields import * 5 | from mongoengine.pymongo_support import * 6 | from mongoengine.context_managers import * 7 | from mongoengine.document import * 8 | 9 | INVISIBLE = TypeVar('INVISIBLE') 10 | 11 | class Base(): 12 | """需要和mongoengine的Document多继承配套使用""" 13 | @staticmethod 14 | def expand_mono(obj): 15 | if hasattr(obj, 'get_base_info'): 16 | return getattr(obj, 'get_base_info')() 17 | else: 18 | return obj 19 | def get_base_info(self, *args): 20 | try: 21 | d = {} 22 | for k in self._fields_ordered: 23 | if get_type_hints(self).get(k, None) == INVISIBLE: 24 | continue 25 | selfk = getattr(self, k) 26 | if isinstance(selfk, list): 27 | for i in selfk: 28 | d.setdefault(k, []).append(Base.expand_mono(i)) 29 | else: 30 | d[k] = Base.expand_mono(selfk) 31 | d['id'] = str(self.id) 32 | return d 33 | except: # 不加注解上面会报错 34 | return self.get_all_info() 35 | def get_all_info(self, *args): 36 | d = {} 37 | for k in self._fields_ordered: 38 | selfk = getattr(self, k) 39 | if isinstance(selfk, list): 40 | for i in selfk: 41 | d.setdefault(k, []).append(Base.expand_mono(i)) 42 | else: 43 | d[k] = Base.expand_mono(selfk) 44 | if hasattr(self, 'id'): 45 | d['id'] = str(self.id) 46 | return d 47 | @classmethod 48 | def chk(cls, pk): 49 | """确保对象存在,如不存在则创建一个,返回给定主键确定的对象""" 50 | if isinstance(pk, cls): 51 | return pk 52 | tmp = cls.objects(pk=pk).first() 53 | if not tmp: 54 | return cls(pk=pk).save() 55 | return tmp 56 | @classmethod 57 | def trychk(cls, pk): 58 | """若对象存在,返回主键对应的对象,否则返回None""" 59 | if isinstance(pk, cls): 60 | return pk 61 | tmp = cls.objects(pk=pk).first() 62 | if not tmp: 63 | return None 64 | return tmp 65 | 66 | class SaveTimeBase(Base): 67 | create_time = DateTimeField() 68 | def save_changes(self): 69 | return self.save() 70 | def first_create(self): 71 | self.create_time = datetime.datetime.now() 72 | return self.save_changes() 73 | 74 | def get_base_info(self, *args): 75 | d = super().get_base_info(*args) 76 | d['create_time'] = self.create_time.strftime('%Y-%m-%d') 77 | return d 78 | 79 | def get_all_info(self, *args): 80 | d = super().get_all_info(*args) 81 | d['create_time'] = self.create_time.strftime('%Y-%m-%d') 82 | return d 83 | -------------------------------------------------------------------------------- /fapi/models/FileStorage.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import datetime 3 | from fapi.models.Auth import * 4 | from fapi.models.Base import * 5 | from mongoengine import * 6 | from mongoengine.fields import (DateTimeField, FileField, IntField, ListField, 7 | ReferenceField, StringField) 8 | 9 | routiner_namemap = {} # 根据名字查找Routiner用 10 | 11 | class FileStorage(Base, Document): 12 | meta = {'allow_inheritance': True} 13 | content = FileField() 14 | filename = StringField() 15 | content_type = StringField() 16 | 17 | class TempFile(FileStorage): 18 | expires = DateTimeField() 19 | async def deleter(self): 20 | await asyncio.sleep( 21 | (self.expires-datetime.datetime.now()).total_seconds() 22 | ) 23 | self.content.delete() 24 | self.delete() 25 | @classmethod 26 | async def resume(cls): 27 | """ 28 | 今后文件多的时候不应按文件删除 29 | 应定时检测再删除 30 | """ 31 | for i in cls.objects(): 32 | asyncio.ensure_future(i.deleter()) 33 | -------------------------------------------------------------------------------- /fapi/models/Player.py: -------------------------------------------------------------------------------- 1 | from mongoengine import * 2 | from typing import Optional, TypeVar, Union, get_type_hints 3 | 4 | from mongoengine.fields import * 5 | from mongoengine.pymongo_support import * 6 | from mongoengine.context_managers import * 7 | from mongoengine.document import * 8 | 9 | from fapi.models.Base import * 10 | from basicutils.algorithms import * 11 | import basicutils.CONST 12 | 13 | INVISIBLE = TypeVar('INVISIBLE') 14 | 15 | class Player(Document): 16 | pid = StringField(primary_key=True) 17 | # aid = ReferenceField(Adapter) 18 | items = DictField() 19 | @staticmethod 20 | def expand_mono(obj): 21 | if hasattr(obj, 'get_base_info'): 22 | return getattr(obj, 'get_base_info')() 23 | else: 24 | return obj 25 | def get_base_info(self, *args): 26 | try: 27 | d = {} 28 | for k in self._fields_ordered: 29 | if get_type_hints(self).get(k, None) == INVISIBLE: 30 | continue 31 | selfk = getattr(self, k) 32 | if isinstance(selfk, list): 33 | for i in selfk: 34 | d.setdefault(k, []).append(Base.expand_mono(i)) 35 | else: 36 | d[k] = Base.expand_mono(selfk) 37 | d['id'] = str(self.id) 38 | return d 39 | except: # 不加注解上面会报错 40 | return self.get_all_info() 41 | # def __int__(self): 42 | # return int(self.pid) 43 | def __str__(self): 44 | return str(self.pk) 45 | def get_all_info(self, *args): 46 | d = {} 47 | for k in self._fields_ordered: 48 | selfk = getattr(self, k) 49 | if isinstance(selfk, list): 50 | for i in selfk: 51 | d.setdefault(k, []).append(Base.expand_mono(i)) 52 | else: 53 | d[k] = Base.expand_mono(selfk) 54 | if hasattr(self, 'id'): 55 | d['id'] = str(self.id) 56 | return d 57 | @classmethod 58 | def chk(cls, pk: str): 59 | # print("CHK", pk, aid) 60 | # print(pk, ) 61 | if isinstance(pk, cls): 62 | return pk 63 | q = cls.objects(pid=pk).first() 64 | if not q: 65 | q = cls(pid=pk).save() 66 | # print("NEW PLAYER", q.get_base_info(), "CREATED!") 67 | # print(q.get_base_info()) 68 | return q 69 | 70 | def upd_credit(self, operator: str, val: float) -> bool: 71 | """修改用户的信用点 72 | 参数: 73 | [str]operator(操作符) 74 | [int]val(操作数) 75 | 返回: 76 | [bool]是否操作成功 77 | """ 78 | if operator not in basicutils.CONST.credit_operators: return False 79 | c = self.items.get('credit', 500) 80 | c, c2 = evaluate_expression(f'{c}{operator}{float(val)}') 81 | c2 = float(c2.strip()) 82 | self.items['credit'] = c2 83 | self.save() 84 | return True 85 | 86 | class RefPlayerBase(Base): 87 | _player = ReferenceField(Player, primary_key=True, reverse_delete_rule=2) 88 | @classmethod 89 | def chk(cls, pk): 90 | if isinstance(pk, Player): 91 | return super().chk(pk) 92 | else: 93 | return super().chk(Player.chk(pk)) 94 | @classmethod 95 | def trychk(cls, pk): 96 | if isinstance(pk, Player): 97 | return super().trychk(pk) 98 | else: 99 | return super().trychk(Player.chk(pk)) 100 | @property 101 | def player(self): 102 | return Player.chk(self._data['_player'].id) -------------------------------------------------------------------------------- /fapi/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/voidf/bot_irori/a112468cae0c229a9b6ed7e4335d7e5a90718732/fapi/models/__init__.py -------------------------------------------------------------------------------- /fapi/routers/__init__.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends 2 | master_router = APIRouter( 3 | # prefix="/api/v2", 4 | tags=["master"], 5 | dependencies=[] 6 | ) 7 | 8 | 9 | from fapi.routers.auth import auth_route 10 | from fapi.routers.worker import worker_route 11 | from fapi.routers.convert import convert_route 12 | from fapi.routers.application import application_route 13 | # from .team import team_route 14 | # from .sign import sign_route 15 | # from .report import report_route 16 | # from .initialize import initialize_route 17 | 18 | 19 | 20 | master_router.include_router(auth_route) 21 | master_router.include_router(worker_route) 22 | master_router.include_router(convert_route) 23 | master_router.include_router(application_route) 24 | # master_router.include_router(team_route) 25 | # master_router.include_router(sign_route) 26 | # master_router.include_router(report_route) 27 | # master_router.include_router(initialize_route) 28 | -------------------------------------------------------------------------------- /fapi/routers/application.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Depends 2 | from fastapi.responses import HTMLResponse 3 | 4 | from cfg import * 5 | 6 | from fapi.models.Auth import * 7 | 8 | from fapi.utils.jwt import * 9 | 10 | import asyncio 11 | application_route = APIRouter( 12 | prefix="/application", 13 | tags=["application - 静态网页应用接口"], 14 | ) 15 | 16 | 17 | @application_route.get('/ws') 18 | async def websocket_page(): 19 | with open('htmlws.htm', 'r') as f: 20 | return HTMLResponse(f.read()) -------------------------------------------------------------------------------- /fapi/routers/auth.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter, Cookie, Depends, HTTPException, Query, Request, Response, File, UploadFile, Form 2 | 3 | from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm 4 | 5 | from pydantic import BaseModel 6 | from cfg import * 7 | 8 | from fapi.models.Auth import * 9 | from fapi.Sessions import SessionManager 10 | 11 | from fapi.utils.jwt import * 12 | 13 | import asyncio 14 | auth_route = APIRouter( 15 | prefix="/auth", 16 | tags=["auth - 网页后台接口"], 17 | ) 18 | 19 | 20 | class login_form(BaseModel): 21 | username: str 22 | password: str 23 | miraiwsurl: str 24 | 25 | # async def try_clear_adapter(a: Adapter): 26 | # if a.username in fapi.G.adapters: 27 | # await fapi.G.adapters[a.username].close() 28 | # return True 29 | # else: 30 | # return False 31 | 32 | import fapi.MiraiSession 33 | #from fapi.MiraiSession import * 34 | async def connect_mirai(miraiwsurl: str) -> int: 35 | return (await SessionManager.new(fapi.MiraiSession.MiraiSession, miraiwsurl)) 36 | 37 | 38 | # @auth_route.post('/legacy') 39 | # async def legacy_auth(f: login_form): 40 | # a = Adapter.objects(username=f.username).first() 41 | # if not a: 42 | # return falseReturn(401, '用户名或密码错误') 43 | 44 | # await Routiner.recover_routiners(a) 45 | 46 | # if not fapi.G.initialized: 47 | # await TempFile.resume() 48 | # fapi.G.initialized = True 49 | # 一次启动上下文 50 | 51 | # if not encrypt(f.password) == a.password: 52 | # return falseReturn(401, '用户名或密码错误') 53 | 54 | # sessionid = await connect_mirai(a, f.miraiwsurl) 55 | # tk = generate_adapter_jwt(a, 86400) 56 | # return {"access_token": tk, "token_type": "bearer", "session_id": sessionid} 57 | 58 | 59 | @auth_route.post('/login') 60 | async def login_auth(rsp: Response, f: OAuth2PasswordRequestForm = Depends(),): 61 | """使用uuid口令连入,获取令牌,用户名和密码都贴上令牌即可""" 62 | tk = f.username if f.username else f.password 63 | if tk != IroriUUID.get().uuid: 64 | return falseReturn(401, '口令错误') 65 | 66 | tk = generate_login_jwt(86400) 67 | rsp.set_cookie("Authorization", tk, 86400) 68 | return {"access_token": tk, "token_type": "bearer"} 69 | 70 | 71 | class register_form(BaseModel): 72 | username: str 73 | password: str 74 | 75 | o2_scheme = OAuth2PasswordBearer(tokenUrl='/auth/login') 76 | 77 | 78 | @auth_route.delete('/logout') 79 | async def logout_session(tk: str = Depends(o2_scheme), session_id: int = Form(...)): 80 | """注销一个session""" 81 | a, msg = verify_login_jwt(tk) 82 | if not a: 83 | return falseReturn(401, msg) 84 | 85 | return trueReturn(data={'isclose': SessionManager.close(session_id)}) 86 | 87 | 88 | @auth_route.post('/mirai') 89 | async def mirai_login(tk: str = Depends(o2_scheme), miraiwsurl: str = Form(...)): 90 | a, msg = verify_login_jwt(tk) 91 | if not a: 92 | return falseReturn(401, msg) 93 | await connect_mirai(miraiwsurl) 94 | return trueReturn() 95 | 96 | from fapi.WebsocketSession import * 97 | from fastapi import WebSocket 98 | from fastapi import status 99 | @auth_route.websocket('/ws') 100 | async def ws_connectin(websocket: WebSocket, token: str = Query(''), typ: str=Query('plain')): 101 | """ 102 | token: player对应的jwt口令,可以通过bot申请 103 | typ: 欲创建的ws连接种类,仅提供json和plain两种 104 | """ 105 | logger.debug(token) 106 | if typ == 'json': 107 | p, msg = verify_player_jwt(token) 108 | if not p: 109 | await websocket.close(status.WS_1008_POLICY_VIOLATION) 110 | await SessionManager.hangon(await SessionManager.new(WebsocketSessionJson, websocket, p.pid)) 111 | elif typ == 'plain': 112 | p, msg = verify_player_jwt(token) 113 | if not p: 114 | await websocket.close(status.WS_1008_POLICY_VIOLATION) 115 | await SessionManager.hangon(await SessionManager.new(WebsocketSessionPlain, websocket, p.pid)) 116 | elif typ.startswith('3rd'): 117 | routiner_reg_player_id = typ.removeprefix('3rd') 118 | if token != IroriUUID.get().uuid: 119 | print(IroriUUID.get().uuid, token) 120 | await websocket.close(status.WS_1008_POLICY_VIOLATION) 121 | await SessionManager.hangon(await SessionManager.new(WebsocketSessionJson, websocket, routiner_reg_player_id)) 122 | elif typ == 'onebot': 123 | if token != IroriUUID.get().uuid: 124 | print(IroriUUID.get().uuid, token) 125 | await websocket.close(status.WS_1008_POLICY_VIOLATION) 126 | await SessionManager.hangon(await SessionManager.new(WebsocketSessionOnebot, websocket)) 127 | else: 128 | await websocket.close(status.WS_1008_POLICY_VIOLATION) 129 | -------------------------------------------------------------------------------- /fapi/routers/convert.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import traceback 3 | import aiohttp 4 | import os 5 | from fastapi.param_functions import Form 6 | import magic 7 | from fapi.models.FileStorage import * 8 | import fastapi 9 | from fastapi import APIRouter, Depends, HTTPException, Request, Response, File, UploadFile 10 | from typing import Optional 11 | from cfg import * 12 | from loguru import logger 13 | from fapi.models.Auth import * 14 | 15 | import asyncio 16 | convert_route = APIRouter( 17 | prefix="/convert", 18 | tags=["convert - 公用媒体转换器"], 19 | ) 20 | 21 | 22 | 23 | 24 | import fapi 25 | 26 | import base64 27 | @convert_route.post('/amr') 28 | async def convert_to_amr(mode: int = 0, f: Optional[UploadFile] = fastapi.File(None), lnk: Optional[str]=Form(''), b64: Optional[str] = Form('')): 29 | """ 30 | format: 31 | 32 | ffmpeg需要根据传入的扩展名确定源格式,一般是三个小写字母 33 | 34 | mode: 35 | 36 | 0: 不裁剪 37 | 38 | 1: 限制质量 39 | 40 | 2: 限制长度""" 41 | return await fapi.to_amr(mode, f, lnk, b64) -------------------------------------------------------------------------------- /fapi/routers/worker.py: -------------------------------------------------------------------------------- 1 | import magic 2 | from fapi.models.FileStorage import FileStorage, TempFile 3 | from typing import Tuple 4 | from loguru import logger 5 | from fapi.utils.jwt import verify_session_jwt 6 | import datetime 7 | import fapi.G 8 | import fastapi 9 | from fastapi import * 10 | # from fastapi import File as fapi_File 11 | from fastapi.responses import * 12 | from pydantic import BaseModel 13 | from cfg import * 14 | 15 | from fapi.models.Auth import * 16 | from fapi import trueReturn, falseReturn 17 | 18 | from basicutils.network import * 19 | from basicutils.chain import * 20 | from fapi.models.Routiner import * 21 | 22 | worker_route = APIRouter( 23 | prefix="/worker", 24 | tags=["worker - 分布式任务执行模块自用,非外部调用接口"], 25 | ) 26 | 27 | 28 | class CoreEntityJson(BaseModel): 29 | ents: str 30 | 31 | 32 | def parse_session_jwt(f: CoreEntityJson) -> CoreEntity: 33 | # logger.critical(f) 34 | ent = CoreEntity.handle_json(f.ents) 35 | # logger.debug(ent) 36 | src, msg = verify_session_jwt(ent.jwt) 37 | ent.jwt = '' 38 | if not src: 39 | return falseReturn(401, msg) 40 | ent.source = src 41 | return ent 42 | 43 | 44 | @worker_route.post('/submit') 45 | async def submit_worker(ent: CoreEntity = Depends(parse_session_jwt)): 46 | ses = SessionManager.get(ent.source) 47 | if ses: 48 | await ses.upload(ent) 49 | return trueReturn() 50 | 51 | oss_route = APIRouter( 52 | prefix="/oss", 53 | tags=["oss - 内置对象储存模块"], 54 | ) 55 | 56 | def save_file_to_mongo(delays: int, fileobj: UploadFile): 57 | # from cfg import db 58 | # from mongoengine import connect 59 | # connect(**db) 60 | fileobj.file.seek(0) 61 | typ = magic.from_buffer(fileobj.file.read(1024), mime=True) 62 | fileobj.file.seek(0) 63 | logger.debug('guessed type: {}', typ) 64 | if delays >= 0: 65 | fs = TempFile( 66 | filename=fileobj.filename, 67 | content_type=typ, 68 | expires=datetime.datetime.now()+datetime.timedelta(seconds=delays) 69 | ) 70 | asyncio.ensure_future(fs.deleter()) 71 | else: 72 | fs = FileStorage( 73 | filename=fileobj.filename, 74 | content_type=typ 75 | ) 76 | fs.content.put(fileobj.file) 77 | fs.save() 78 | return fs 79 | 80 | @oss_route.post('') 81 | async def upload_oss(delays: int = -1, fileobj: UploadFile = fastapi.File(...), ents: str = Form(...)): 82 | parse_session_jwt(CoreEntityJson(ents=ents)) 83 | logger.debug('file name: {}', fileobj.filename) 84 | logger.debug('file type: {}', fileobj.content_type) 85 | return {'url': str(save_file_to_mongo(delays, fileobj).pk)} 86 | 87 | 88 | @oss_route.get('/{fspk}') 89 | async def download_oss(fspk: str): 90 | fs: FileStorage = FileStorage.trychk(fspk) 91 | 92 | if not fs: 93 | return falseReturn(404, 'No such resource') 94 | else: 95 | return Response(fs.content.read(), media_type=fs.content_type) 96 | 97 | 98 | @oss_route.delete('/{fspk}') 99 | async def delete_oss(fspk: str, ent: CoreEntity = Depends(parse_session_jwt)): 100 | fs: FileStorage = FileStorage.trychk(fspk) 101 | if not fs: 102 | return falseReturn(404, 'No such resource') 103 | fs.content.delete() 104 | fs.delete() 105 | return trueReturn() 106 | 107 | worker_route.include_router(oss_route) 108 | 109 | routiner_route = APIRouter( 110 | prefix="/routiner", 111 | tags=["routiner - 内置日程器模块"], 112 | ) 113 | 114 | 115 | async def resolve_routiner(ent: CoreEntity = Depends(parse_session_jwt)) -> Tuple[CoreEntity, Routiner]: 116 | return ent, fapi.models.Routiner.routiner_namemap[ent.meta.get('routiner')] 117 | 118 | 119 | @routiner_route.post('') 120 | async def create_routine(tp: Tuple[CoreEntity, Routiner] = Depends(resolve_routiner)): 121 | """创建日程器""" 122 | ent, R = tp 123 | # pid = str(ent.player) 124 | # R = ent.meta.get('routiner') 125 | res = await R.add(ent) 126 | ent.chain = MessageChain.auto_make(f'【订阅器】{R}创建成功') 127 | await SessionManager.autoupload(ent) 128 | return {'res': res} 129 | 130 | 131 | @routiner_route.delete('') 132 | async def delete_routine(tp: Tuple[CoreEntity, Routiner] = Depends(resolve_routiner)): 133 | """销毁日程器(取消订阅)""" 134 | ent, R = tp 135 | res = await R.cancel(ent) 136 | ent.chain = MessageChain.auto_make(f'【订阅器】{R}删除成功') 137 | await SessionManager.autoupload(ent) 138 | return {'res': res} 139 | 140 | 141 | @routiner_route.options('') 142 | async def options_routine(tp: Tuple[CoreEntity, Routiner] = Depends(resolve_routiner)): 143 | """调用日程器的内部功能""" 144 | ent, R = tp 145 | F = ent.meta.get('call') 146 | logger.info(F) 147 | logger.info(R) 148 | logger.info(R.call_map) 149 | # ent.meta['aid'] = str(src) 150 | # ent.meta['pid'] = str(ent.player) 151 | if hasattr(R, 'call_map') and F in R.call_map: 152 | res = await R.call_map[F](ent) 153 | logger.debug(f'return: {res}') 154 | return {'res': res} 155 | return falseReturn(404, "No such method") 156 | 157 | worker_route.include_router(routiner_route) 158 | -------------------------------------------------------------------------------- /fapi/utils/jwt.py: -------------------------------------------------------------------------------- 1 | from fapi.webcfg import salt, jwt_key 2 | import hashlib 3 | import datetime 4 | from jose import jwt 5 | import traceback 6 | from loguru import logger 7 | from fapi.models.Player import Player 8 | from typing import * 9 | from fapi.Sessions import * 10 | 11 | def encrypt(s: str) -> str: 12 | return hashlib.sha256((s + salt).encode('utf-8')).hexdigest() 13 | 14 | def generate_login_jwt(expires: float=86400): 15 | return jwt.encode( 16 | { 17 | 'ts': str((datetime.datetime.now()+ datetime.timedelta(seconds=expires)).timestamp()) 18 | }, # payload, 有效载体 19 | jwt_key, # 进行加密签名的密钥 20 | ) 21 | def verify_login_jwt(token): 22 | try: 23 | payload = jwt.decode(token, jwt_key) 24 | if datetime.datetime.now().timestamp() > float(payload['ts']): 25 | return None, "令牌过期" 26 | return True, "" 27 | except: 28 | logger.critical(traceback.format_exc()) 29 | return None, "非预期错误" 30 | 31 | def generate_player_jwt(pid: str): 32 | 33 | return jwt.encode( 34 | { 35 | 'pid': pid, 36 | 'token': Player.objects(pid=pid).first().items.get('token', ''), 37 | }, # payload, 有效载体 38 | jwt_key, # 进行加密签名的密钥 39 | ) 40 | 41 | def verify_player_jwt(token) -> Tuple[Player, str]: 42 | try: 43 | payload = jwt.decode(token, jwt_key) 44 | plr: Player = Player.objects(pid=payload['pid']).first() 45 | if not plr: 46 | return None, "用户不存在" 47 | if not plr.items.get('token', '') == payload['token']: 48 | return None, "令牌无效" 49 | return plr, "" 50 | except: 51 | logger.critical(traceback.format_exc()) 52 | return None, "非预期错误" 53 | 54 | def generate_session_jwt(sid: int, expire_seconds: float = 120.0): 55 | token_dict = { 56 | 'sid': sid, 57 | 'ts': str((datetime.datetime.now()+ datetime.timedelta(seconds=expire_seconds)).timestamp()) 58 | } 59 | return jwt.encode( 60 | token_dict, # payload, 有效载体 61 | jwt_key, # 进行加密签名的密钥 62 | ) 63 | def verify_session_jwt(token): 64 | try: 65 | payload = jwt.decode(token, jwt_key) 66 | if datetime.datetime.now().timestamp() > float(payload['ts']): 67 | return None, "令牌过期" 68 | session = payload['sid'] 69 | if not SessionManager.get(session): 70 | return None, "无此会话" 71 | return session, "" 72 | except: 73 | logger.critical(traceback.format_exc()) 74 | return None, "非预期错误" 75 | 76 | # from fastapi import Request 77 | # async def verify_login_cookie(auth: Request): 78 | # """验证cookie中的login令牌""" 79 | # toberaise = HTTPException(400, "数据错误") 80 | # try: 81 | # logger.debug(auth.client.host) 82 | # Authorization = auth.cookies.get('Authorization', None) 83 | # if Authorization: 84 | # sta, msg = verify_login_jwt(Authorization) 85 | # if sta: 86 | # return 87 | # toberaise = HTTPException(401, msg) 88 | # toberaise = HTTPException(401, "没有令牌") 89 | # except: 90 | # logger.critical(traceback.format_exc()) 91 | # raise toberaise 92 | 93 | 94 | # def generate_adapter_jwt(adapter: Union[Adapter, str], expire_seconds: float = 120.0): 95 | # token_dict = { 96 | # 'aid': str(adapter.pk) if isinstance(adapter, Adapter) else adapter, 97 | # 'ts': str((datetime.datetime.now()+ datetime.timedelta(seconds=expire_seconds)).timestamp()) 98 | # } 99 | # return jwt.encode( 100 | # token_dict, # payload, 有效载体 101 | # jwt_key, # 进行加密签名的密钥 102 | # ) 103 | 104 | # def verify_adapter_jwt(token): 105 | # try: 106 | # payload = jwt.decode(token, jwt_key) 107 | # if datetime.datetime.now().timestamp() > float(payload['ts']): 108 | # return None, "令牌过期" 109 | # adapter = Adapter.objects(pk=payload['aid']).first() 110 | # if not adapter: 111 | # return None, "无此用户" 112 | # return adapter, "" 113 | # except: 114 | # traceback.print_exc() 115 | # return None, "数据错误" 116 | 117 | 118 | -------------------------------------------------------------------------------- /fapi/utils/media.py: -------------------------------------------------------------------------------- 1 | 2 | from fastapi import UploadFile, Form 3 | import fastapi 4 | import aiohttp 5 | import magic 6 | import base64 7 | from basicutils.media import generateTmpFileName 8 | from fapi.models.FileStorage import * 9 | import os 10 | import traceback 11 | from loguru import logger 12 | import datetime 13 | import asyncio 14 | from typing import * 15 | def nolimitAudioSize(src, extension) -> str: 16 | dst = generateTmpFileName(ext='.amr') 17 | if extension in ("mid", "midi"): 18 | os.system(f'timidity {src} -Ow -o - | ffmpeg -y -i - -codec amr_nb -ac 1 -ar 8000 {dst}') 19 | else: 20 | os.system(f'ffmpeg -y -i {src} -codec amr_nb -ac 1 -ar 8000 {dst}') 21 | # asyncio.ensure_future(rmTmpFile(dst)) 22 | return dst 23 | 24 | def limitAudioSizeByBitrate(src, extension) -> str: 25 | """依赖ffmpeg,生成一个临时文件,全 损 音 质""" 26 | # lim = 8 * 1024 # 即1MB,大于1M发不出去 27 | lim = 8000 28 | dst = generateTmpFileName(ext='.amr') 29 | dur = os.popen(f'ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 {src}').read() 30 | dur = float(dur) 31 | print(dur) 32 | if extension in ("mid", "midi"): 33 | os.system(f'timidity {src} -Ow -o - | ffmpeg -y -i - -codec amr_nb -ac 1 -ar 8000 -b:a {lim / dur}k {dst}') 34 | else: 35 | os.system(f'ffmpeg -y -i {src} -codec amr_nb -ac 1 -ar 8000 -b:a {lim / dur}k {dst}') 36 | # asyncio.ensure_future(rmTmpFile(dst)) 37 | return dst 38 | 39 | def limitAudioSizeByCut(src, extension) -> str: 40 | """超出部分会被剪掉""" 41 | dst = generateTmpFileName(ext='.amr') 42 | if extension in ("mid", "midi"): 43 | os.system(f'timidity {src} -Ow -o - | ffmpeg -y - -codec amr_nb -ac 1 -ar 8000 -fs 1000K {dst}') 44 | else: 45 | os.system(f'ffmpeg -y -i {src} -codec amr_nb -ac 1 -ar 8000 -fs 1000K {dst}') 46 | # asyncio.ensure_future(rmTmpFile(dst)) 47 | return dst 48 | 49 | async def to_amr(mode: int = 0, f: Optional[UploadFile] = fastapi.File(None), lnk: Optional[str]=Form(''), b64: Optional[str] = Form('')): 50 | fname = f'tmp{datetime.datetime.now().timestamp()}' 51 | with open(fname, 'wb') as fi: 52 | if lnk: 53 | ses = aiohttp.ClientSession() 54 | try: 55 | async with ses.get(lnk) as resp: 56 | fi.write(await resp.content.read()) 57 | except: 58 | raise 59 | elif b64: 60 | fi.write(base64.b64decode(b64)) 61 | else: 62 | fi.write(await f.read()) 63 | try: 64 | typ = magic.from_file(fname, mime=True) 65 | logger.debug(magic.from_file(fname)) 66 | logger.debug(magic.from_file(fname, mime=True)) 67 | 68 | if typ == 'application/octet-stream': 69 | fn = fname + '.amr' 70 | os.rename(fname, fn) 71 | ret = fn 72 | fname = fn 73 | else: 74 | ext = typ.split('/')[1] 75 | fn = fname + '.' + ext 76 | os.rename(fname, fn) 77 | fname = fn 78 | ret = [ 79 | nolimitAudioSize, 80 | limitAudioSizeByBitrate, 81 | limitAudioSizeByCut 82 | ][mode](fname, ext) 83 | with open(ret, 'rb') as fi: 84 | t = TempFile( 85 | filename=ret, 86 | content_type='audio/AMR', 87 | expires=datetime.datetime.now()+datetime.timedelta(seconds=30) 88 | ) 89 | t.content.put(fi) 90 | t.save() 91 | asyncio.ensure_future(t.deleter()) 92 | os.remove(fname) 93 | os.remove(ret) 94 | return {'url': str(t.pk)} 95 | except: 96 | os.remove(fname) 97 | try: 98 | os.remove(ret) 99 | except: 100 | pass 101 | raise 102 | -------------------------------------------------------------------------------- /fapi/utils/syscall.py: -------------------------------------------------------------------------------- 1 | from basicutils.network import * 2 | import os 3 | from io import StringIO 4 | from contextlib import redirect_stdout 5 | # https://stackoverflow.com/questions/3906232/python-get-the-print-output-in-an-exec-statement 6 | 7 | async def sys_exec(ent: CoreEntity, args: list): 8 | import fapi 9 | import fapi.routers 10 | import fapi.models 11 | from fapi.models.Routiner import Routiner 12 | from fapi.Sessions import SessionManager 13 | f = StringIO() 14 | with redirect_stdout(f): 15 | exec(' '.join(args)) 16 | return f.getvalue() 17 | async def sys_eval(ent: CoreEntity, args: list): 18 | import fapi 19 | import fapi.routers 20 | import fapi.models 21 | from fapi.models.Routiner import Routiner 22 | from fapi.Sessions import SessionManager 23 | return f"""{eval(' '.join(args))}""" 24 | async def sys_run(ent: CoreEntity, args: list): return f"""{os.popen(' '.join(args)).read()}""" 25 | async def sys_help(ent: CoreEntity, args: list): return '目前仅支持exec, eval, run, send四个命令' 26 | async def sys_unauthorized(ent: CoreEntity, args: list): return "您没有权限执行此调用" 27 | -------------------------------------------------------------------------------- /ffmpeg-get-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | apt install -y cmake git g++ timidity flite1-dev libgsm* libsdl2-dev libavresample-dev libcdio-paranoia-dev libcdio-dev libcdio-dev libjack-dev libpocketsphinx-dev libomxil-bellagio-dev libopenal-dev libzvbi-dev libzmq3-dev libxvidcore-dev libx265-dev libx264-dev libwebp-dev libvpx-dev libvidstab-dev libtwolame-dev libtheora-dev libspeex-dev libssh-dev libsoxr-dev libsnappy-dev libshine-dev librubberband-dev librsvg2-dev librabbitmq-dev libpulse-dev libopus-dev libopenmpt-dev libopenjp2-7-dev libopencore-amrnb-dev libopencore-amrwb-dev libmysofa-dev libmp3lame-dev libchromaprint-dev libmfx-dev libgme* yasm frei0r-plugins-dev libunistring-dev gnutls-dev ladspa-sdk-dev libaom-dev liblilv-dev libiec61883-dev libavc1394-dev libass-dev libbluray-dev libbs2b-dev libcaca-dev libcodec2-dev libdav1d-dev libdc1394-dev libdrm-dev pkg-config 3 | git clone https://github.com/FFmpeg/FFmpeg.git 4 | 5 | cd FFmpeg 6 | ./configure --prefix=/usr --enable-static --extra-version=5 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --disable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-sdl2 --enable-pocketsphinx --enable-libmfx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared --enable-libopencore-amrnb --enable-version3 7 | make 8 | make install 9 | # timidity tmp43ZC.mid -Ow -o - | ffmpeg -i - -ac 1 -ar 8000 aaa.amr -------------------------------------------------------------------------------- /ffmpeg.conf: -------------------------------------------------------------------------------- 1 | 2 | 3 | ./configure --prefix=/usr --extra-version=5 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu 4 | --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --disable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-pocketsphinx --enable-libmfx --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared --enable-libopencore-amrnb --enable-version3 -------------------------------------------------------------------------------- /htmlws.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 27 | 28 | 29 |

WS测试

30 |
31 |
32 | 33 | 34 | 35 | 36 |
37 | 38 |
39 |
40 | 命令执行情况 41 |
42 | 43 |
44 |
45 | 46 | 47 |

你的工作路径:

here 48 |
49 |
50 | 51 | 127 | 128 | 129 | 140 | -------------------------------------------------------------------------------- /install_docker_centos.sh: -------------------------------------------------------------------------------- 1 | sudo yum install -y yum-utils 2 | sudo yum-config-manager \ 3 | --add-repo \ 4 | https://download.docker.com/linux/centos/docker-ce.repo 5 | sudo yum install docker-ce docker-ce-cli containerd.io 6 | sudo systemctl start docker 7 | sudo docker run hello-world 8 | -------------------------------------------------------------------------------- /install_docker_debian.sh: -------------------------------------------------------------------------------- 1 | sudo apt-get install -y \ 2 | apt-transport-https \ 3 | ca-certificates \ 4 | curl \ 5 | gnupg-agent \ 6 | software-properties-common 7 | sudo curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - 8 | sudo apt-key fingerprint 0EBFCD88 9 | sudo add-apt-repository \ 10 | "deb [arch=amd64] https://download.docker.com/linux/debian \ 11 | $(lsb_release -cs) \ 12 | stable" 13 | sudo apt-get update 14 | sudo apt-get -y install docker-ce docker-ce-cli containerd.io 15 | -------------------------------------------------------------------------------- /irori-OSS.py: -------------------------------------------------------------------------------- 1 | from fastapi import * 2 | 3 | from fastapi.responses import * 4 | 5 | # from pydantic import BaseModel 6 | 7 | from mongoengine import * 8 | 9 | import cfg 10 | 11 | connect(**cfg.OSSdb) 12 | 13 | app = FastAPI() 14 | 15 | class FileStorage(Document): 16 | # fname = StringField() 17 | content = FileField() 18 | 19 | @app.post('/upload') 20 | async def upload_(authkey: str, f: UploadFile = File(...)): 21 | if authkey != cfg.upload_key: 22 | return HTTPException(401) 23 | fs = FileStorage() 24 | print(f.filename) 25 | print(f.content_type) 26 | 27 | 28 | fs.content.put(f.file) 29 | fs.save() 30 | return {'url': cfg.oss_host + 'download/' + str(fs.pk)} 31 | 32 | # bs = FileStorage(content=f.file.read()) 33 | 34 | @app.get('/download/{fspk}') 35 | async def download_(fspk: str): 36 | fs = FileStorage.objects(pk=fspk).first() 37 | if not fs: 38 | return HTTPException(404) 39 | else: 40 | return Response(fs.content.read()) 41 | 42 | # uvicorn irori-OSS:app --host 0.0.0.0 --port 11111 --ssl-keyfile ssl/A.key --ssl-certfile ssl/A.crt 43 | if __name__ == '__main__': 44 | import uvicorn 45 | uvicorn.run(app) 46 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pydantic 2 | numpy==1.22.0 3 | bs4 4 | requests[socks] 5 | qrcode 6 | pillow 7 | selenium 8 | json5 9 | terminaltables==3.1.0 10 | colorclass==2.2.0 11 | mido 12 | pexpect 13 | devtools 14 | xlrd==1.2.0 15 | mongoengine 16 | pyzipper 17 | websockets 18 | # python-magic-bin==0.4.14 -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | from celery import Celery 2 | 3 | app = Celery('tasks', broker='pyamqp://guest@localhost//',backend="mongodb://127.0.0.1:27017/irori_taskqueue") 4 | 5 | 6 | @app.task 7 | def add(x, y): 8 | return x + y -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup as BS 3 | import basicutils.CONST as C 4 | import datetime 5 | import json 6 | 7 | def make_req2(dvd: int, typ: str='BTC'): 8 | ima = datetime.datetime.now() 9 | prv = ima - datetime.timedelta(days=dvd) 10 | r = requests.get( 11 | f'''https://api.nasdaq.com/api/quote/{typ}/historical?assetclass=crypto&fromdate={prv.strftime('%Y-%m-%d')}&limit={dvd}&todate={ima.strftime('%Y-%m-%d')}''', 12 | headers={ 13 | "accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 14 | "accept-encoding":"gzip, deflate, br", 15 | "accept-language":"zh-CN,zh;q=0.9", 16 | "cache-control":"no-cache", 17 | "dnt":"1", 18 | "pragma":"no-cache", 19 | "sec-fetch-mode":"navigate", 20 | "sec-fetch-site":"none", 21 | "sec-fetch-user":"?1", 22 | "upgrade-insecure-requests":"1", 23 | "user-agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 24 | } 25 | ) 26 | res = r.text 27 | print(json.loads(res)) 28 | return res 29 | 30 | def fetch_cryptocurrency_info(typ: str = 'BTC'): 31 | lnk = f'https://api.nasdaq.com/api/quote/{typ}/info?assetclass=crypto' 32 | r = requests.get(lnk, headers={ 33 | "accept":"application/json, text/plain, */*", 34 | "accept-encoding":"gzip, deflate, br", 35 | "accept-language":"zh-CN,zh;q=0.9", 36 | "cache-control":"no-cache", 37 | "dnt":"1", 38 | "origin":"https://www.nasdaq.com", 39 | "pragma":"no-cache", 40 | "referer":"https://www.nasdaq.com/market-activity/cryptocurrency/btc", 41 | "sec-fetch-dest":"empty", 42 | "sec-fetch-mode":"cors", 43 | "sec-fetch-site":"same-site", 44 | "user-agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36" 45 | }) 46 | j = json.loads(r.text) 47 | report = (f"币种:{j['data']['companyName']}\n" 48 | f"今日最高:{j['data']['keyStats']['High']['value']}\n" 49 | f"今日最低:{j['data']['keyStats']['Low']['value']}\n" 50 | f"现在价格:{j['data']['primaryData']['lastSalePrice']}\n" 51 | f"刷新时间:{j['data']['primaryData']['lastTradeTimestamp']}\n" 52 | f"变动幅度:{j['data']['primaryData']['percentageChange']}") 53 | 54 | print(json.loads(r.text)) 55 | return report 56 | def get_cryptocurrencies(): 57 | r = requests.get( 58 | f'''https://www.nasdaq.com/market-activity/cryptocurrency''', 59 | headers={ 60 | "accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", 61 | "accept-encoding":"gzip, deflate, br", 62 | "accept-language":"zh-CN,zh;q=0.9", 63 | "cache-control":"no-cache", 64 | "dnt":"1", 65 | "pragma":"no-cache", 66 | "sec-fetch-mode":"navigate", 67 | "sec-fetch-site":"none", 68 | "sec-fetch-user":"?1", 69 | "upgrade-insecure-requests":"1", 70 | "user-agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36" 71 | } 72 | ) 73 | res = r.text 74 | # print(res) 75 | b = BS(res, 'html.parser') 76 | ccs = [i['data-symbol'] for i in b('tr', attrs={'data-asset-class':'cryptocurrency'})] 77 | print(ccs) 78 | return ccs 79 | # print(make_req2(1)) 80 | # print(fetch('ETH')) 81 | print(get_cryptocurrencies()) 82 | -------------------------------------------------------------------------------- /test2.py: -------------------------------------------------------------------------------- 1 | """ 2 | (Python >= 3.6) 3 | This is an example of how to prompt inside an application that uses the asyncio 4 | eventloop. The ``prompt_toolkit`` library will make sure that when other 5 | coroutines are writing to stdout, they write above the prompt, not destroying 6 | the input line. 7 | This example does several things: 8 | 1. It starts a simple coroutine, printing a counter to stdout every second. 9 | 2. It starts a simple input/echo app loop which reads from stdin. 10 | Very important is the following patch. If you are passing stdin by reference to 11 | other parts of the code, make sure that this patch is applied as early as 12 | possible. :: 13 | sys.stdout = app.stdout_proxy() 14 | """ 15 | 16 | import asyncio 17 | 18 | from prompt_toolkit.patch_stdout import patch_stdout 19 | from prompt_toolkit.shortcuts import PromptSession 20 | 21 | from loguru import logger 22 | 23 | logger.add(lambda msg: print()) 24 | 25 | 26 | import logging 27 | import sys 28 | # logger.add(sys.stdout) 29 | async def print_counter(): 30 | """ 31 | Coroutine that prints counters. 32 | """ 33 | try: 34 | i = 0 35 | while True: 36 | # print("Counter: %i" % i) 37 | logger.info(f'i\n') 38 | # logging.info(i*i) 39 | # print() 40 | # print() 41 | i += 1 42 | await asyncio.sleep(3) 43 | except asyncio.CancelledError: 44 | print("Background task cancelled.") 45 | 46 | 47 | async def interactive_shell(): 48 | """ 49 | Like `interactive_shell`, but doing things manual. 50 | """ 51 | # Create Prompt. 52 | session = PromptSession("Say something: ") 53 | 54 | # Run echo loop. Read text from stdin, and reply it back. 55 | while True: 56 | try: 57 | result = await session.prompt_async() 58 | print('You said: "{0}"'.format(result)) 59 | except (EOFError, KeyboardInterrupt): 60 | return 61 | 62 | 63 | async def main(): 64 | with patch_stdout(True): 65 | background_task = asyncio.ensure_future(print_counter()) 66 | try: 67 | await interactive_shell() 68 | finally: 69 | background_task.cancel() 70 | print("Quitting event loop. Bye.") 71 | 72 | 73 | if __name__ == "__main__": 74 | try: 75 | from asyncio import run 76 | except ImportError: 77 | asyncio.run_until_complete(main()) 78 | else: 79 | asyncio.run(main()) -------------------------------------------------------------------------------- /test4_celery.py: -------------------------------------------------------------------------------- 1 | from Worker import task 2 | from basicutils.socketutils import * 3 | 4 | def teststr(s: str): 5 | print(f"<<<\t[{s}]") 6 | res = task.delay(CoreEntity.wrap_strchain(s).json()) 7 | 8 | resp: CoreEntity = CoreEntity.handle_json(res.get(timeout=3)) 9 | res.forget() 10 | print(f">>>\t{resp.chain.tostr()}\n") 11 | 12 | 13 | # Math 14 | teststr('#h #C') 15 | teststr('#C 4 2') 16 | teststr('#h #A') 17 | teststr('#A 4 3') 18 | teststr('#h #K') 19 | teststr('#K 5') 20 | teststr('#h #stat') 21 | teststr('#stat 1 3 2.5') 22 | -------------------------------------------------------------------------------- /testinvoke.py: -------------------------------------------------------------------------------- 1 | import pickle, random, collections, string, re, requests, os 2 | 3 | def func(rawinputs: str): 4 | """#约稿 [#waifu, #召唤, #产粮] 5 | ai画图,txt2img,容易被封所以还是建议优先直接用网页 6 | """ 7 | ses = requests.session() 8 | authdir = 'waifusd_auth.pickle' 9 | apibase = 'http://127.0.0.1:7860' 10 | if os.path.exists(authdir): 11 | with open(authdir, 'rb') as f: 12 | usr, pw = pickle.load(f) 13 | ses.post(apibase+'/login', data={'username':usr,'password':pw}) 14 | 15 | 16 | filter_p = re.compile('<.*?>', re.MULTILINE) 17 | def filter(src): 18 | b = [] 19 | for i in filter_p.sub(' ', src).replace('\n', ' ').strip(): 20 | if b[-1:] == [' '] and ' ' == i: 21 | continue 22 | else: 23 | b.append(i) 24 | return ''.join(b) 25 | 26 | def parser(src): 27 | pm = [ 28 | 'prompt:', 'negative prompt:', 'steps:', 'sampler:', 'cfg scale:', 29 | 'seed:', 'size:', 'model hash:', 'denoising strength:', 'clip skip:', 30 | ] 31 | b = [[] for i in pm] 32 | 33 | cur = b[0] 34 | p = 0 35 | 36 | def chk(): 37 | nonlocal p 38 | nonlocal cur 39 | for ind, token in enumerate(pm): 40 | if src[p:p+len(token)].lower() == token: 41 | p += len(token) 42 | cur = b[ind] 43 | return False 44 | return True 45 | 46 | while p < len(src): 47 | if chk(): 48 | cur.append(src[p]) 49 | p += 1 50 | for p, i in enumerate(b): 51 | b[p] = ''.join(i).strip().removesuffix(',') 52 | return {j:i for i, j in zip(b, pm) if i} 53 | 54 | if rawinputs == '样例': 55 | with open('Assets/waifusd/prompts.pickle', 'rb') as f: 56 | li = pickle.load(f) 57 | return random.choice(li) 58 | if rawinputs == '词汇表': 59 | with open('Assets/waifusd/chat_tokens.txt', 'r', encoding='utf-8') as f: 60 | li = f.read().split('\n') 61 | return '\n'.join(random.sample(li, 10)) 62 | if rawinputs == '模板': 63 | return """((masterpiece)), best quality, illustration, 1 girl, beautiful,beautiful detailed sky, catgirl,beautiful detailed water, cinematic lighting, Ice Wings, (few clothes),loli,(small breasts),light aero blue hair, Cirno(Touhou), wet clothes,underwater,hold breath,bubbles,cat ears ,dramatic angle 64 | Negative prompt: lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, bad feet, huge breasts 65 | Steps: 75, Sampler: DDIM, CFG scale: 11, Seed: 3323485853, Size: 512x768, Model hash: e6e8e1fc, Clip skip: 2""" 66 | 67 | txt2img_inputs = collections.namedtuple('txt2img_inputs', 68 | field_names=[ 69 | "prompt", "negative_prompt", "prompt_style", "prompt_style2", "steps", 70 | "sampler", "restore_faces", "tiling", "batch_count", "batch_size", 71 | "cfg_scale", "seed", "sub_seed", "subseed_strength", "seed_resize_from_h", 72 | "seed_resize_from_w", "unk_1", "width", "height", "highres_fix", 73 | "scale_latent", "denoising_strength", "script", 74 | ], defaults=[ 75 | "loli", "nsfw", "None", "None", 30, 76 | "DDIM", False, False, 1, 1, 77 | 7, -1, -1, 0, 0, 78 | 0, False, 512, 512, False, 79 | False, 0.85, "None", 80 | ]) 81 | 82 | def nums(src): 83 | b = [] 84 | for i in src: 85 | if i in string.digits+'-': 86 | b.append(i) 87 | return int(''.join(b)) 88 | def floats(src): 89 | b = [] 90 | for i in src: 91 | if i in string.digits+'.': 92 | b.append(i) 93 | return float(''.join(b)) 94 | 95 | mapping_string = { # 迫真萃取 96 | float: floats, 97 | int: nums, 98 | str: lambda x: x 99 | } 100 | 101 | parsed = parser(filter(rawinputs)) 102 | if len(parsed) > 1: # 有东西,高级模式 103 | modify = {} 104 | for k, v in parsed.items(): 105 | pk = k[:-1].replace(' ', '_') 106 | if pk in txt2img_inputs._fields: 107 | modify[pk] = mapping_string[type(txt2img_inputs._field_defaults[pk])](v) 108 | 109 | if sz := parsed.get('size:', ''): 110 | w, h = re.compile('([0-9]+)[x\*]([0-9]+)').search(sz).groups() 111 | modify['width'] = int(w) 112 | modify['height'] = int(h) 113 | model = txt2img_inputs(**modify) 114 | else: # 简易模式 115 | model = txt2img_inputs( 116 | prompt='((masterpiece)), best quality, illustration, beautiful, beautiful detailed eyes,' + parsed['prompt:'], 117 | negative_prompt='nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry' 118 | ) 119 | args = model + ( 120 | False, False, None, "", "Seed", 121 | "", "Nothing", "", True, False, 122 | False, None, 123 | "", # json like object 124 | "" # html like object 125 | ) 126 | # return args 127 | # print(args) 128 | jj = ses.post(f"{apibase}/api/predict", json={'fn_index':13, 'data':args}).json() 129 | print(jj) 130 | j = ses.post(f"{apibase}/api/predict", json={'fn_index':13, 'data':args}).json()['data'] 131 | return j 132 | return [Image(base64=j[0][0][22:]), Plain(filter(j[2]))] 133 | 134 | 135 | def banner(): print("==========================") 136 | 137 | a1 = func("""robot,blue,gun,death""") 138 | banner() 139 | a2 = func("""masterpiece, best quility ,official art,extremely detailed CG unity 8k wallpaper,delicate scene,2 young girl,symmetrical docking,hug,beautiful detailed eyes, look at the viewer,small breasts,undressing 140 | Negative prompt: lowres, bad anatomy, bad hands,text, error, missing fingers,extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry,dick,cum 141 | Steps: 32, Sampler: Heun, CFG scale: 12, Seed: 84833071, Size: 512x768, Model hash: e6e8e1fc, Hypernet: anime_3, Clip skip: 2""") 142 | print(a1,'\n', a2) 143 | for p, (i, j) in enumerate(zip(a1, a2)): 144 | if (type(i)!=type(j)): 145 | print('Err', p, type(i), type(j)) --------------------------------------------------------------------------------