Commit 5568a190 authored by nanahira's avatar nanahira

current

parent 2d37fc6f
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
/build
/output
.git*
.dockerignore
Dockerfile
.gitlab-ci.yml
/config.yaml
.idea
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
/build
/output
/config.yaml
.idea
stages:
- build
- combine
- deploy
variables:
GIT_DEPTH: "1"
CONTAINER_TEST_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
CONTAINER_TEST_ARM_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-arm
CONTAINER_TEST_X86_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG-x86
CONTAINER_RELEASE_IMAGE: $CI_REGISTRY_IMAGE:latest
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
build-x86:
stage: build
tags:
- docker
script:
- docker build --pull -t $CONTAINER_TEST_X86_IMAGE .
- docker push $CONTAINER_TEST_X86_IMAGE
build-arm:
stage: build
tags:
- docker-arm
script:
- docker build --pull -t $CONTAINER_TEST_ARM_IMAGE .
- docker push $CONTAINER_TEST_ARM_IMAGE
combine:
stage: combine
tags:
- docker
script:
- docker pull $CONTAINER_TEST_X86_IMAGE
- docker pull $CONTAINER_TEST_ARM_IMAGE
- docker manifest create $CONTAINER_TEST_IMAGE --amend $CONTAINER_TEST_X86_IMAGE --amend $CONTAINER_TEST_ARM_IMAGE
- docker manifest push $CONTAINER_TEST_IMAGE
deploy_latest:
stage: deploy
tags:
- docker
script:
- docker pull $CONTAINER_TEST_IMAGE
- docker tag $CONTAINER_TEST_IMAGE $CONTAINER_RELEASE_IMAGE
- docker push $CONTAINER_RELEASE_IMAGE
only:
- master
deploy_tag:
stage: deploy
tags:
- docker
variables:
CONTAINER_TAG_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
script:
- docker pull $CONTAINER_TEST_IMAGE
- docker tag $CONTAINER_TEST_IMAGE $CONTAINER_TAG_IMAGE
- docker push $CONTAINER_TAG_IMAGE
only:
- tags
/install-npm.sh
.git*
/output
/dest
/config.yaml
.idea
.dockerignore
Dockerfile
\ No newline at end of file
{
"singleQuote": true,
"trailingComma": "all"
}
\ No newline at end of file
FROM node:buster-slim
LABEL Author="Nanahira <nanahira@momobako.com>"
RUN apt update && apt -y install python3 build-essential && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
WORKDIR /usr/src/app
COPY ./package*.json ./
RUN npm ci
COPY . ./
RUN npm run build
CMD ["npm", "run", "start"]
This diff is collapsed.
#!/bin/bash
npm install --save \
lodash
npm install --save-dev \
@types/node \
@types/lodash \
typescript \
'@typescript-eslint/eslint-plugin@^4.28.2' \
'@typescript-eslint/parser@^4.28.2 '\
'eslint@^7.30.0' \
'eslint-config-prettier@^8.3.0' \
'eslint-plugin-prettier@^3.4.0' \
prettier
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "koishi-plugin-act",
"version": "1.0.0",
"description": "",
"main": "build/index.js",
"dependencies": {
"class-transformer": "^0.4.0",
"delay": "^5.0.0",
"lodash": "^4.17.21",
"mustache": "^4.2.0"
},
"devDependencies": {
"@types/lodash": "^4.14.172",
"@types/mustache": "^4.1.2",
"@types/node": "^16.4.10",
"@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.29.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"prettier": "^2.3.2",
"typescript": "^4.3.5"
},
"scripts": {
"lint": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git@git.mycard.moe:nanahira/koishi-plugin-act.git"
},
"author": "Nanahira",
"license": "ISC"
}
import { PlaybookLine } from './PlaybookLine';
export class Character {
playFun: (line: PlaybookLine, text: string) => Promise<void>;
constructor(
public id: number,
public name: string,
public displayName?: string,
) {
if (!displayName) {
this.displayName = name;
}
}
async play(line: PlaybookLine, text: string) {
if (this.playFun) {
return this.playFun(line, text);
} else {
return;
}
}
}
import { PlaybookLine } from './PlaybookLine';
import { matchHeader, TuneTimeOptions } from './utility';
import { Character } from './Character';
import { Transform } from 'class-transformer';
export class Playbook {
characters: Character[] = [];
lines: PlaybookLine[] = [];
constructor(public characterNameMap: Record<string, string> = {}) {}
getCharacterDisplayName(name: string) {
if (this.characterNameMap[name]) {
return this.characterNameMap[name];
} else {
return name;
}
}
resetTiming() {
const firstLineTime = this.lines[0].time;
if (!firstLineTime) {
return;
}
for (const line of this.lines) {
line.time -= firstLineTime;
}
}
fixupCharacters() {
const characterMap = new Map<string, boolean>();
let currentCharacterIndex = 0;
for (const line of this.lines) {
if (!characterMap.has(line.name)) {
this.characters.push(
new Character(
currentCharacterIndex++,
line.name,
this.getCharacterDisplayName(line.name),
),
);
characterMap.set(line.name, true);
}
}
for (const line of this.lines) {
line.transferName(this.characters);
}
}
loadText(text: string) {
const lines = text.trim().split(/\r?\n/);
let lastLineIndex = -1;
for (let i = 0; i < lines.length; ++i) {
const line = lines[i];
if (!line.length) {
continue;
}
if (matchHeader(line)) {
if (lastLineIndex > 0 && i - lastLineIndex > 1) {
this.lines.push(
PlaybookLine.FromLines(lines.slice(lastLineIndex, i)),
);
}
lastLineIndex = i;
}
}
const finalIndex = lines.length;
if (lastLineIndex > 0 && finalIndex - lastLineIndex > 1) {
this.lines.push(
PlaybookLine.FromLines(lines.slice(lastLineIndex, finalIndex)),
);
}
this.resetTiming();
this.fixupCharacters();
}
private findPreviousSelfLine(i) {
const currentLine = this.lines[i];
for (let j = i - 1; j >= 0; j--) {
const tryLine = this.lines[j];
if (tryLine.characterId === currentLine.characterId) {
return tryLine;
}
}
return null;
}
tuneTime(options: TuneTimeOptions) {
if (!this.lines.length) {
return;
}
this.lines[0].time = 0;
for (let i = 1; i < this.lines.length; ++i) {
const currentLine = this.lines[i];
const previousLine = this.lines[i - 1];
const selfPreviousLine = this.findPreviousSelfLine(i);
const randomAddValue = Math.ceil(Math.random() * options.maxRandomDelay);
const possibleReadTiming =
previousLine.time +
options.readSlope * previousLine.renderContent(this.characters).length +
options.readIntercept;
let possibleWriteTiming = 0;
if (selfPreviousLine) {
possibleWriteTiming =
selfPreviousLine.time +
options.writeSlope *
currentLine.renderContent(this.characters).length +
options.writeIntercept;
}
currentLine.time =
Math.max(possibleReadTiming, possibleWriteTiming) + randomAddValue;
}
}
async play(tick: number) {
const currentLines = this.lines.filter((l) => l.time === tick);
for (const line of currentLines) {
await line.play(this.characters);
}
return !this.lines.length || tick >= this.lines[this.lines.length - 1].time;
}
}
import { matchHeader, render } from './utility';
import { Character } from './Character';
export class PlaybookLine {
characterId?: number;
name?: string;
content: string;
time: number;
static FromLines(lines: string[]) {
const l = new PlaybookLine();
l.fromLines(lines);
return l;
}
fromLines(lines: string[]) {
const header = lines[0];
this.content = lines
.slice(1)
.filter((l) => l.length)
.join('\n');
const headerMatch = matchHeader(header);
if (!headerMatch) {
this.name = header;
return;
}
this.name = headerMatch[1];
this.time =
parseInt(headerMatch[4]) * 3600 +
parseInt(headerMatch[5]) * 60 +
parseInt(headerMatch[6]);
if (
(headerMatch[3] === '下午' || headerMatch[8] === 'PM') &&
headerMatch[4] !== '12'
) {
this.time += 12 * 3600;
}
}
transferName(characters: Character[]) {
this.characterId = characters.findIndex((c) => c.name === this.name);
for (let i = 0; i < characters.length; ++i) {
this.content = this.content.replace(
new RegExp(characters[i].name, 'g'),
`{{${i}.displayName}}`,
);
}
this.name = undefined;
}
renderContent(characters: Character[]) {
return render(this.content, characters);
}
play(characters: Character[]) {
const character = characters.find((c) => c.id === this.characterId);
return character.play(this, this.renderContent(characters));
}
}
import Mustache from 'mustache';
export function matchHeader(input: string) {
return input
.trim()
.match(/^(.+?) ((上午|下午) ?)?(\d{1,2}):(\d{1,2}):(\d{1,2})( (AM|PM))?$/);
}
export function render(template: string, view: any) {
return Mustache.render(template, view, null, { escape: (v) => v });
}
export interface TuneTimeOptions {
readSlope: number;
readIntercept: number;
writeSlope: number;
writeIntercept: number;
maxRandomDelay: number;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
import * as fs from 'fs';
import { Playbook } from '../src/Playbook';
import { classToPlain, plainToClass } from 'class-transformer';
import delay from 'delay';
async function main() {
const content = await fs.promises.readFile(
'./test/test-playbook.txt',
'utf-8',
);
const playbook = new Playbook({ Andria: 'Nanahira' });
playbook.loadText(content);
playbook.tuneTime({
readSlope: 0,
readIntercept: 1,
writeSlope: 1,
writeIntercept: 2,
maxRandomDelay: 5,
});
const plain = classToPlain(playbook);
console.log(JSON.stringify(plain, null, 2));
playbook.characters.forEach(
(c) =>
(c.playFun = async (l, text) =>
console.error(`${c.displayName}: ${text}`)),
);
for (let i = 0; ; i++) {
if (await playbook.play(i)) {
break;
}
await delay(1000);
}
}
main();
Andria 14:24:00
把汉络的手放到自己薄纱睡裙里面任由对方抚摸揉捏丰盈绵软的双乳~
Andria 14:24:02
嗯w
Andria 14:27:14
啾咪
汉络 14:27:34
这样可爱的挑逗
汉络 14:27:48
会让我忍不住的呀,你这小坏蛋
Andria 14:27:50
才没挑逗呢❤️
汉络 14:28:20
(开始把香软的双乳在手中变换的形状玩弄)
Andria 14:30:52
呜~❤️
Andria 14:31:18
面红耳赤着吐出着半截小舌头任惹对方肆意的玩弄揉捏双乳了w..❤️
汉络 14:33:12
淫荡又可爱的坏孩子(轻轻拉动着薄纱睡裙的绳结随之轻轻滑落)
Andria 14:34:36
嗯唔~❤️
Andria 14:35:25
本就可以隐隐约约窥探个够自己身姿的睡衣被汉络熟练的解开滑落,里面是完完全全的真空呢..❤️
汉络 14:35:46
真的是...我忍不住了
Andria 14:35:57
好哦..❤️
汉络 14:36:49
(随即将自己的猎物扑倒在身下 肆意的亲吻锁骨 双手也让一个香乳在手中变换着形状)
Andria 14:36:50
软软的依赖在怀里用胸前温暖的软肉压迫在对方的胸口蹭蹭蹭❤️
Andria 14:38:06
作为猎物被扑倒在身下双手搂住对方的脖子,任了吻住自己的脖颈肆意的揉捏着丰盈的双乳w❤️
汉络 14:44:10
(迫不及待的擒住对方的软腰 晃动着自己胯下的巨物 炽热的阳具贴在大腿上不断摸索着)
Andria 14:44:56
唔~已经..湿漉漉的想要了啦❤️
汉络 14:45:21
想什么啊?小骚货(轻轻的捏着乳尖往上颠了一下)
Andria 14:45:25
脸红着被把住腰肢妖媚的微微扭动着~任了温柔的摸索自己的大腿惹呜❤️
Andria 14:47:38
汉络的小骚货..沐雨想要被汉络肏淫乱的小色穴~❤️
被捏住硬硬的乳头拽一把~让自己忍不住的吐出来着半截小舌头淫乱的娇喘出声了❤️
汉络 14:48:21
真的是,发情了吗(肉棒顶在蜜穴上龟头撑开蜜穴 先前的乳白液体也有些许滑落了出来)
Andria 14:48:44
唔姆~已经..没办法了啦~来肏坏掉小骚货沐雨❤️
Andria 14:48:59
手指分开着自己的阴唇惹❤️
汉络 14:50:20
你这样子很骚~像欠干的婊子一样唉♥(羞辱般扶着肉棒根部 将前端拍打在蜜穴上 挑逗着对方花枝乱颤扭动身躯)
Andria 14:52:45
唔姆~不要~不要捉弄咱了啦❤️
面红耳赤着捂着脸明明是被挑逗羞辱淫穴居然愈发湿润了呜❤️脸上羞红的淫穴却不停的溢出淫水被肉棒怕啪啪啪的抽打着唔❤️
汉络 14:53:56
那快点把大腿分开来迎接主人的肉棒吧,反正你已经饥渴到不行了吧~
汉络 14:54:17
让主人狠狠的肏你的骚穴吧
Andria 14:54:38
嗯唔~麻烦惹❤️请..请把恋肏坏吧❤️
M大开着双腿用手指分开淫嫩的阴唇了唔❤️
Andria 14:55:38
主人~汉络大人~小骚货沐雨要❤️
汉络 14:57:22
你这骚货 就当我的生殖工具吧(扶着先前还沾着淫水的肉棒 兴奋的肉棒已经胀大且青筋暴起 二次的尺寸明显比先前粗大了许多 对准穴口便毫不留情的直接推入)
汉络 14:59:00
小骚货沐雨 看看自己现在的样子 娇媚的年轻女孩子的身体被一根粗大的肉棒插着,真的是淫荡死了(巨大的肉棒一下贯穿层层障碍填满整个骚穴 惹得原本接待平坦的小腹此刻已经能够明显的看见形状 兴奋的肉棒在内部不断跳动着)
Andria 15:01:31
唔~已经是汉络形状的小骚货了~被大肉棒塞满淫穴~好舒服~咱是汉络的小淫娃❤️
肚子上面浮现着肉棒侵入的凸起,淫穴紧紧的吸裹着兴奋的肉棒不停颤抖着无比舒适唔❤️
Andria 15:02:39
骚穴献媚的紧紧吸裹着大肉棒,完全是汉络的形状了献媚的吸裹着呜❤️
汉络 15:05:29
你这骚货这么勾引我,现在又夹的这么紧,是不是想把我的精液全部榨干出来啊,天呐,你那没有精液就躁动不已的子宫(掏出手机对着胯下的“肉便器” 拍摄着无比淫荡的春色 胯下的巨物也没停着 不断侵犯和撞击着年轻女孩的肉体 翘臀上的软肉也随着撞击荡开一层层涟漪 整个人如同铃铛般有节奏地晃动着)
Andria 15:08:56
嗯唔~咱已经是子宫没有被汉络精液注满就躁动不停的淫娃惹~我要~汉络主人~射满骚货沐雨的淫穴..麻烦惹❤️
在主人身下被干的乱七八糟毫无尊严的双手比着✌🏻作为不停发情到淫娃肉便器在身下被肉棒激烈的顶撞着发情中的淫穴媚态百出的让自己变成主人的样子
汉络 15:12:40
这种程度还是榨不出来的呢(抬起手狠狠的拍打白嫩的翘臀 兴奋的使可人收缩夹紧肉棒 也似乎更为深入几分直接使用宫颈获取快感)
Andria 15:13:45
呜呀!❤️
Andria 15:14:19
被抽打着翘臀受惊一样嫩穴也愈发紧致献媚的吸裹着肉棒惹..❤️
Andria 15:14:40
被完全填满了脑子乱七八糟的❤️
汉络 15:15:39
真棒啊,这温柔又毫无缝隙的纠缠,你这家伙离开男人的肉棒就会发情,插入看来就会更兴奋了(抱着小腰如同使用飞机杯一般快速的蹂躏着)
Andria 15:16:31
噫!已经..乱七八糟惹~汉络sama~还要❤️
Andria 15:17:04
双手搂紧紧着缠在汉络身上被把着腰肢宛如使用飞机杯一样激烈的抽插蹂躏惹呜❤️
汉络 15:21:33
年轻的女孩子被征服在自己的胯下,这件事情光想想就兴奋了!何况还是你这么淫荡的坏孩子,干脆直接当我的精厕算了(喘着粗气 将对方遏在自己的身下 高高的抬起又重重的塞满 粗大的肉棒撑开整个蜜穴 完全将视频下的可人征服)
Andria 15:23:19
呜噫!❤️这样..这种事情~汉络~来惩罚淫乱的坏孩子❤️
身体颤抖着被死死压制在身下激烈的狠狠抽插着把蜜穴完全塞满满了呜,都变成肉棒的样子惹❤️
汉络 15:24:02
还想要更激烈吗?你这条母狗♥
Andria 15:25:03
呜~已经..已经乱七八糟了..汪❤️
Andria 15:25:12
反着白眼吐出着半截舌头乱七八糟呜
汉络 15:27:39
那我来送你最后的冲刺吧♥(随着一阵快速的抽送 一阵快感像电流一样从脊髓传遍了全身 接着整个人感觉像是被抛到了半空中的失重的失神状态 然后好像放烟火一样快感从末梢神经一点一点的炸裂开来,最后一阵剧烈的大爆炸淹没了身体 令其淫乱的小脑袋瞬间一片空白)
Andria 15:30:44
噫❤️!
Andria 15:31:23
完全被快感弄得神志不清精神涣散掉躺在床上只剩下下意识的身体颤抖了呜..
Andria 15:31:56
脑子里面一片空白吐着半截小舌头已经乱七八糟惹..❤️
汉络 15:32:58
这么久,下面已经有甜蜜的酸酥了吧(沿着阴蒂搅动着分身)
汉络 15:33:29
想想我现在在射出来灌满你淫乱的身体还是继续好好的侵犯你呢
Andria 15:34:08
呜..
Andria 15:34:19
已经..已经..不行..惹
Andria 15:34:43
身体颓软在床上,身体酥麻腿软着软塌塌呜..
汉络 15:36:46
那就让主人来给你第二次受精吧 让你以后看见主人就想得到胯下的肉棒(兴奋的扬起手拍打上了阴蒂 继续欺凌者如同被玩坏的玩偶的沐雨 直到对方翻着白眼吐着小舌头苦苦的说着淫荡的话语哀求 才将浓稠的精液灌满子宫 随后也不拔出来,停靠在蜜穴里面,趴在对方的身上)
Andria 15:45:40
呜..❤️
Andria 15:45:54
反着白眼身体颓软软塌塌..惹❤️
汉络 15:45:59
好了哦~(亲亲额头)
\ No newline at end of file
{
"compilerOptions": {
"outDir": "build",
"module": "commonjs",
"target": "esnext",
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"declaration": true,
"sourceMap": true
},
"compileOnSave": true,
"allowJs": true,
"include": [
"*.ts",
"src/**/*.ts",
"test/**/*.ts"
]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment