├── .vscode ├── solution-explorer │ ├── class.ts-template │ ├── default.ts-template │ ├── interface.ts-template │ ├── class.cs-template │ ├── enum.cs-template │ ├── interface.cs-template │ ├── class.vb-template │ ├── template-parameters.js │ └── template-list.json ├── tasks.json └── launch.json ├── Resources ├── voc_list_kiso_kansei.csv ├── ngwords.txt └── greeting.json ├── bin └── Debug │ ├── net5.0 │ └── publish │ │ └── Citrine.dll │ └── netstandard2.1 │ └── publish │ └── Citrine.dll ├── Const.cs ├── .gitignore ├── .editorconfig ├── Citrine.csproj ├── README.md ├── README-ja.md ├── Commands ├── EchoCommand.cs ├── IsAdminCommand.cs ├── WakachiCommand.cs ├── ReverseCommand.cs ├── PipeLineCommand.cs ├── DateTimeCommand.cs ├── UnyaizeCommand.cs ├── StopCommand.cs ├── VersionCommand.cs ├── WrapWithCommand.cs ├── RandomModule.cs ├── UserAgentCommand.cs ├── NyaizeCommand.cs ├── FujiwaraTatsuyaCommand.cs ├── ModuleCommand.cs ├── InspectCommand.cs ├── WaCommand.cs ├── BalloonGenCommand.cs ├── DebugCommand.cs ├── ToCharCommand.cs ├── KatakanaCommand.cs ├── HiraganaCommand.cs ├── DumpCommand.cs ├── TranslateCommand.cs ├── OjisanCommand.cs ├── PipeCommand.cs ├── RatingModule.cs ├── NmoudameCommand.cs ├── WordCommand.cs ├── DiceCommand.cs ├── HelpCommand.cs └── BinCommand.cs ├── Modules ├── EmptyMentionHandlerModule.cs ├── GreetingModule.command.cs ├── GoodRoundDetectorModule.cs ├── OjichatModule.cs ├── CallMeModule.cs ├── ImHereModule.cs ├── WeatherModule.cs ├── EraitModule.cs ├── Markov │ ├── MarkovNode.cs │ └── TinySegmenter.cs ├── FollowBackModule.cs ├── CommandModule.cs ├── IntegrationFamilyModule.cs ├── BackupModule.cs ├── StatusModule.cs ├── ValentineModule.cs ├── SushiModule.cs ├── HarassmentHandlerModule.cs ├── ReactModule.cs ├── UserMovingKit.cs ├── JankenModule.cs ├── GreetingModule.cs ├── KawaiiModule.cs ├── FortuneModule.cs ├── BirthdayModule.cs ├── EconomyModule.cs ├── TranslateModule.cs ├── SearchModule.cs ├── ApexLegendsModule.cs └── FortuneModule.strings.cs └── LICENSE /.vscode/solution-explorer/class.ts-template: -------------------------------------------------------------------------------- 1 | export class {{name}} { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/solution-explorer/default.ts-template: -------------------------------------------------------------------------------- 1 | export default {{name}} { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/solution-explorer/interface.ts-template: -------------------------------------------------------------------------------- 1 | export interface {{name}} { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /Resources/voc_list_kiso_kansei.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EbiseLutica/Citrine/HEAD/Resources/voc_list_kiso_kansei.csv -------------------------------------------------------------------------------- /bin/Debug/net5.0/publish/Citrine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EbiseLutica/Citrine/HEAD/bin/Debug/net5.0/publish/Citrine.dll -------------------------------------------------------------------------------- /bin/Debug/netstandard2.1/publish/Citrine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EbiseLutica/Citrine/HEAD/bin/Debug/netstandard2.1/publish/Citrine.dll -------------------------------------------------------------------------------- /.vscode/solution-explorer/class.cs-template: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace {{namespace}} 4 | { 5 | public class {{name}} 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/solution-explorer/enum.cs-template: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace {{namespace}} 4 | { 5 | public enum {{name}} 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Const.cs: -------------------------------------------------------------------------------- 1 | namespace Citrine.Core 2 | { 3 | public static class Const 4 | { 5 | public static readonly string Version = "1.3.1"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/solution-explorer/interface.cs-template: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace {{namespace}} 4 | { 5 | public interface {{name}} 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/solution-explorer/class.vb-template: -------------------------------------------------------------------------------- 1 | Imports System 2 | 3 | Namespace {{namespace}} 4 | 5 | Public Class {{name}} 6 | 7 | End Class 8 | 9 | End Namespace 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/* 2 | !bin/Debug 3 | bin/Debug/* 4 | !bin/Debug/net5.0 5 | bin/Debug/net5.0/* 6 | !bin/Debug/net5.0/publish 7 | bin/Debug/net5.0/publish/* 8 | !bin/Debug/net5.0/publish/Citrine.dll 9 | obj 10 | 11 | Citrine/token 12 | token 13 | admin 14 | nicknames 15 | *.log 16 | config.json 17 | storage.json 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [.yml] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | 15 | [/disboard/**] 16 | charset = unset 17 | end_of_line = unset 18 | insert_final_newline = unset 19 | trim_trailing_whitespace = unset 20 | indent_style = unset 21 | indent_size = unset 22 | -------------------------------------------------------------------------------- /Citrine.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net5.0 4 | Latest 5 | Enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Citrine 2 | 3 | シトリンは、可愛くて賢い chatbot な BotBone プラグインです。 4 | 5 | ## 使用法 6 | 7 | まずはビルド。 8 | 9 | ``` 10 | dotnet build 11 | ``` 12 | 13 | BotBoneの /plugins フォルダに、Citrine.dllを導入 14 | BotBone を起動するだけ 15 | 16 | ## 貢献 17 | 18 | - [バグ報告 / 要望](//github.com/xeltica/citrine/issues/new) 19 | - [プルリクエスト](//github.com/xeltica/citrine/compare) 20 | 21 | ### 以前のコントリビューター 22 | 23 | [その他...](//github.com/Xeltica/Citrine/graphs/contributors) 24 | 25 | ## ライセンス 26 | 27 | [MIT License](LICENSE) 28 | -------------------------------------------------------------------------------- /.vscode/solution-explorer/template-parameters.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | 3 | module.exports = function(filename, projectPath, folderPath) { 4 | var namespace = "Unknown"; 5 | if (projectPath) { 6 | namespace = path.basename(projectPath, path.extname(projectPath)); 7 | if (folderPath) { 8 | namespace += "." + folderPath.replace(path.dirname(projectPath), "").substring(1).replace(/[\\\/]/g, "."); 9 | } 10 | namespace = namespace.replace(/[\\\-]/g, "_"); 11 | } 12 | 13 | return { 14 | namespace: namespace, 15 | name: path.basename(filename, path.extname(filename)) 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /README-ja.md: -------------------------------------------------------------------------------- 1 | # Citrine 2 | 3 | [English](README.md) ・ 日本語 4 | 5 | シトリンは、可愛くて賢い chatbot な BotBone プラグインです。 6 | 7 | ## 使用法 8 | 9 | まずはビルド。 10 | 11 | ``` 12 | dotnet build 13 | ``` 14 | 15 | BotBoneの /plugins フォルダに、[Citrine.dll](bin/Debug/netstandard2.1/publish/Citrine.dll) を導入 16 | BotBone を起動するだけ 17 | 18 | ## 貢献 19 | 20 | - [バグ報告 / 要望](//github.com/xeltica/citrine/issues/new) 21 | - [プルリクエスト](//github.com/xeltica/citrine/compare) 22 | 23 | ### 以前のコントリビューター 24 | 25 | - @u1-liquid 26 | 27 | [その他...](//github.com/Xeltica/Citrine/graphs/contributors) 28 | 29 | ## ライセンス 30 | 31 | [MIT License](LICENSE) 32 | -------------------------------------------------------------------------------- /Commands/EchoCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core 8 | { 9 | public class EchoCommand : CommandBase 10 | { 11 | public override string Name => "echo"; 12 | 13 | public override string Usage => "/echo "; 14 | 15 | public override string Description => "引数をそのままオウム返しします。"; 16 | 17 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 18 | { 19 | return body; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Modules/EmptyMentionHandlerModule.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using BotBone.Core; 3 | using BotBone.Core.Api; 4 | using BotBone.Core.Modules; 5 | 6 | namespace Citrine.Core.Modules 7 | { 8 | public class EmptyMentionHandlerModule : ModuleBase 9 | { 10 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 11 | { 12 | // n.Text.TrimMentions() がカラッポであり 13 | // n.Reply.User.Id == n.User.Id であればハンドルする 14 | if (string.IsNullOrEmpty(n.Text?.TrimMentions()) && n.Reply?.User.Id == n.User.Id) 15 | { 16 | await core.HandleMentionAsync(n.Reply); 17 | return true; 18 | } 19 | return false; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Commands/IsAdminCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core 8 | { 9 | public class IsAdminCommand : CommandBase 10 | { 11 | public override string Name => "isadmin"; 12 | 13 | public override string Usage => "/isadmin"; 14 | 15 | public override string Description => "管理者であるかどうか取得します"; 16 | 17 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 18 | { 19 | return sender.IsAdmin ? "yes" : "no"; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Commands/WakachiCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using Citrine.Core.Modules.Markov; 6 | 7 | namespace Citrine.Core 8 | { 9 | public class WakachiCommand : CommandBase 10 | { 11 | public override string Name => "wakachi"; 12 | public override string Usage => "/wakachi "; 13 | public override string Description => "入力文字列をわかち書きします。"; 14 | 15 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 16 | { 17 | return string.Join(" ", TinySegmenter.Instance.Segment(body)); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Commands/ReverseCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | using BotBone.Core.Modules; 7 | 8 | namespace Citrine.Core 9 | { 10 | public class ReverseCommand : CommandBase 11 | { 12 | public override string Name => "reverse"; 13 | 14 | public override string Usage => "/reverse "; 15 | 16 | public override string Description => "テキストを逆さに変換します。"; 17 | 18 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 19 | { 20 | return string.Concat(body.Reverse()); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Commands/PipeLineCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core 8 | { 9 | public class PipeLineCommand : CommandBase 10 | { 11 | public override string Name => "pipeline"; 12 | 13 | public override string Usage => "/pipeline (これ以降に改行で区切ってコマンドを書いて下さい。)"; 14 | 15 | public override string Description => "各種コマンドを連結して実行します。前のコマンドの出力は、次のコマンドの末尾に追記されます。"; 16 | 17 | public override Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 18 | { 19 | return PipeCommand.RunPipeAsync(sender, core, body, '|'); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Commands/DateTimeCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | using BotBone.Core.Modules; 7 | 8 | namespace Citrine.Core 9 | { 10 | public class DateTimeCommand : CommandBase 11 | { 12 | public override string Name => "datetime"; 13 | 14 | public override string Usage => "/datetime"; 15 | 16 | public override string Description => "現在時刻を返します。"; 17 | 18 | public override string[] Aliases => new[] { "date", "time", "dt" }; 19 | 20 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 21 | { 22 | return DateTime.Now.ToString(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Commands/UnyaizeCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core 8 | { 9 | public class UnyaizeCommand : CommandBase 10 | { 11 | public override string Name => "unnyaize"; 12 | 13 | public override string Usage => "/unnyaize"; 14 | 15 | public override string[] Aliases => new []{ "unnya" }; 16 | 17 | public override string Description => "発言のねこを除去します。"; 18 | 19 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 20 | { 21 | return body.Replace("にゃ", "な").Replace("ニャ", "ナ").Replace("ニャ", "ナ"); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Commands/StopCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core 8 | { 9 | public class StopCommand : CommandBase 10 | { 11 | public override string Name => "stop"; 12 | 13 | public override string Usage => "/stop"; 14 | 15 | public override PermissionFlag Permission => PermissionFlag.AdminOnly; 16 | 17 | public override string Description => "シトリンを停止します。"; 18 | 19 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 20 | { 21 | if (sender is PostCommandSender s) 22 | await shell.ReplyAsync(s.Post, "またねー。"); 23 | Environment.Exit(0); 24 | return null; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Commands/VersionCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core 8 | { 9 | public class VersionCommand : CommandBase 10 | { 11 | public override string Name => "version"; 12 | 13 | public override string Usage => "/version or /ver or /v"; 14 | 15 | public override string[] Aliases { get; } = { "ver", "v" }; 16 | 17 | public override string Description => "バージョン情報を取得します。"; 18 | 19 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 20 | { 21 | return $"BotBone v{Server.Version} Citrine v{Const.Version}"; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Modules/GreetingModule.command.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | 7 | namespace Citrine.Core.Modules 8 | { 9 | public partial class GreetingModule 10 | { 11 | public string Name => "greetings"; 12 | 13 | public string[] Aliases => Array.Empty(); 14 | 15 | public bool IgnoreCase => false; 16 | 17 | public PermissionFlag Permission => PermissionFlag.AdminOnly; 18 | 19 | public string Usage => ""; 20 | 21 | public string Description => ""; 22 | 23 | public async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 24 | { 25 | await Task.Delay(0); 26 | return string.Join(", ", patterns.Select(p => p.Regex)); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Commands/WrapWithCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core 8 | { 9 | public class WrapWithCommand : CommandBase 10 | { 11 | public override string Name => "wrapwith"; 12 | 13 | public override string Usage => "/wrapwith "; 14 | 15 | public override string Description => "指定した文字列で囲まれた文字列を返します。"; 16 | 17 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 18 | { 19 | if (args.Length < 2) 20 | throw new CommandException(); 21 | body = body[(args[0].Length + 1)..]; 22 | return args[0] + body + args[0]; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Commands/RandomModule.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | using Newtonsoft.Json; 9 | 10 | namespace Citrine.Core 11 | { 12 | public class RandomModule : CommandBase 13 | { 14 | public override string Name => "random"; 15 | 16 | public override string Usage => "/random "; 17 | 18 | public override string Description => "指定した引数の中からどれか1つを返します。"; 19 | 20 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 21 | { 22 | if (args.Length == 0) throw new CommandException(); 23 | 24 | return args.Random(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Commands/UserAgentCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Text; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | using BotBone.Core.Modules; 7 | 8 | namespace Citrine.Core 9 | { 10 | public class UserAgentCommand : CommandBase 11 | { 12 | public override string Name => "useragent"; 13 | 14 | public override string Usage => "/useragent or /ua"; 15 | 16 | public override string[] Aliases { get; } = { "ua" }; 17 | 18 | public override string Description => "シトリンが使用する HTTP Client のユーザーエージェントを取得します。"; 19 | 20 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 21 | { 22 | return Server.Http.DefaultRequestHeaders.UserAgent.ToString(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Commands/NyaizeCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System; 3 | using System.Threading.Tasks; 4 | using System.Web; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | using Newtonsoft.Json.Linq; 9 | 10 | namespace Citrine.Core 11 | { 12 | public class NyaizeCommand : CommandBase 13 | { 14 | public override string Name => "nyaize"; 15 | 16 | public override string Usage => "/nyaize"; 17 | 18 | public override string[] Aliases => new []{ "nya" }; 19 | 20 | public override string Description => "発言をねこにします。"; 21 | 22 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 23 | { 24 | return body.Replace("な", "にゃ").Replace("ナ", "ニャ").Replace("ナ", "ニャ"); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Commands/FujiwaraTatsuyaCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | using BotBone.Core.Modules; 7 | 8 | namespace Citrine.Core 9 | { 10 | public class FujiwaraTatsuyaCommand : CommandBase 11 | { 12 | public override string Name => "fujiwaratatsuya"; 13 | 14 | public override string Usage => "/fujiwaratatsuya"; 15 | 16 | public override string[] Aliases => new []{ "fujiwara", "fjwr", "fujitatsu" }; 17 | 18 | public override string Description => "テ゛キ゛ス゛ト゛を゛返゛す゛だ゛け゛の゛コ゛マ゛ン゛ド゛だ゛ぞ゛。"; 19 | 20 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 21 | { 22 | return string.Concat(body.Select(c => c + "゛")); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Commands/ModuleCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core 10 | { 11 | public class ModuleCommand : CommandBase 12 | { 13 | public override string Name => "modules"; 14 | 15 | public override string Usage => "/modules or /mods"; 16 | 17 | public override string[] Aliases { get; } = { "mods" }; 18 | 19 | public override string Description => "全モジュールを列挙します。"; 20 | 21 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 22 | { 23 | var mods = core.Modules.Select(mod => mod.GetType().Name); 24 | return $"モジュール数: {mods.Count()}\n{string.Join(",", mods)}"; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Modules/GoodRoundDetectorModule.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using BotBone.Core; 3 | using BotBone.Core.Api; 4 | using BotBone.Core.Modules; 5 | 6 | namespace Citrine.Core.Modules 7 | { 8 | /// 9 | /// キリ番ノート検出モジュール 10 | /// 11 | public class GoodRoundDetectorModule : ModuleBase 12 | { 13 | public override async Task OnTimelineAsync(IPost n, IShell shell, Server core) 14 | { 15 | if (core.GetRatingOf(n.User) < Rating.Like) return false; 16 | // 投稿についているユーザー名は完璧では無い 17 | // なのでシェルを用いて完全体を持ってくる 18 | var user = await shell.GetUserAsync(n.User.Id); 19 | if (user == null) return false; 20 | 21 | if (user.PostsCount > 0 && user.PostsCount % 10000 == 0) 22 | { 23 | await shell.SendDirectMessageAsync(user, $"{core.GetNicknameOf(user)},{user.PostsCount}投稿達成おめでとう."); 24 | } 25 | 26 | return false; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Commands/InspectCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Linq; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core 10 | { 11 | public class InspectCommand : CommandBase 12 | { 13 | public override string Name => "inspect"; 14 | 15 | public override string Usage => "/inspect [commands]"; 16 | 17 | public override string Description => "コマンドの引数をそのまま列挙します。"; 18 | 19 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 20 | { 21 | var builder = new StringBuilder(); 22 | builder.AppendFormat("[{0}]: ", args.Length); 23 | builder.Append(string.Join(", ", args.Select(a => $"\"{a}\""))); 24 | return builder.ToString(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Modules/OjichatModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | using Newtonsoft.Json; 9 | 10 | namespace Citrine.Core.Modules 11 | { 12 | public class OjichatModule : ModuleBase 13 | { 14 | public static readonly string StatOjisanedCount = "stat.ojisaned-count"; 15 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 16 | { 17 | if (n.Text is string text && text.IsMatch("おじさんの(真似|まね)")) 18 | { 19 | EconomyModule.Pay(n, shell, core); 20 | await Task.Delay(4000); 21 | core.LikeWithLimited(n.User); 22 | core.Storage[n.User].Add(StatOjisanedCount); 23 | await shell.ReplyAsync(n, await core.ExecCommand("/ojisan " + core.GetNicknameOf(n.User))); 24 | return true; 25 | } 26 | return false; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Commands/WaCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | 7 | namespace Citrine.Core 8 | { 9 | public class WaCommand : CommandBase 10 | { 11 | public override string Name => "wa"; 12 | 13 | public override string Usage => "/wa [amount=15]"; 14 | 15 | public override string Description => "#わーーーーーーーーーーーーーーー"; 16 | 17 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 18 | { 19 | var amount = args.Length < 1 ? 15 : int.Parse(args[0]); 20 | 21 | return string.Concat(Wa(amount)); 22 | } 23 | 24 | private IEnumerable Wa(int amount) 25 | { 26 | yield return '#'; 27 | yield return 'わ'; 28 | for (var i = 0; i < amount; i++) 29 | yield return 'ー'; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Commands/BalloonGenCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core 10 | { 11 | public class BalloonGenCommand : CommandBase 12 | { 13 | public override string Name => "balloongen"; 14 | 15 | public override string Usage => "/balloongen <好きな文字列>"; 16 | 17 | public override string[] Aliases { get; } = { "balloon-gen", "balloon", "genballoon" }; 18 | 19 | public override string Description => "ギザギザ吹き出しを作ります。"; 20 | 21 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 22 | { 23 | body = " " + body + " "; 24 | var crown = ".\n_" + new string('人', body.Length) + "_\n"; 25 | var pate = ">" + body + "<\n"; 26 | var heel = " ̄" + string.Concat(Enumerable.Repeat("Y^", body.Length - 1)) + "Y ̄"; 27 | return crown + pate + heel; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Commands/DebugCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core 10 | { 11 | public class DebugCommand : CommandBase 12 | { 13 | public override string Name => "debug"; 14 | 15 | public override string Usage => "/debug"; 16 | 17 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 18 | { 19 | if (sender is not PostCommandSender p) return "call from post"; 20 | 21 | switch (args[0]) 22 | { 23 | case "set": 24 | core.Storage[p.User].Set(args[1], args[2]); 25 | return "success"; 26 | case "get": 27 | return (core.Storage[p.User].Get(args[1], (object)"null")).ToString(); 28 | case "has": 29 | return core.Storage[p.User].Has(args[1]).ToString(); 30 | default: 31 | return "set / get / has"; 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Commands/ToCharCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System; 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | using System.Threading.Tasks; 6 | using BotBone.Core; 7 | using BotBone.Core.Api; 8 | using BotBone.Core.Modules; 9 | 10 | namespace Citrine.Core 11 | { 12 | public class ToCharCommand : CommandBase 13 | { 14 | public override string Name => "tochar"; 15 | 16 | public override string Usage => "/tochar "; 17 | 18 | public override string Description => "16進文字列をテキストに変換します。"; 19 | 20 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 21 | { 22 | body = Regex.Replace(body, @"\s", ""); 23 | if (body.Length % 2 != 0) 24 | return "正しい16進文字列ではありません."; 25 | 26 | var data = new byte[body.Length / 2]; 27 | for (var i = 0; i < body.Length / 2; i++) 28 | { 29 | data[i] = Convert.ToByte(body.Substring(i * 2, 2), 16); 30 | } 31 | 32 | return Encoding.UTF8.GetString(data); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Commands/KatakanaCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core 10 | { 11 | public class KatakanaCommand : CommandBase 12 | { 13 | public override string Name => "katakana"; 14 | 15 | public override string Usage => "/katakana "; 16 | 17 | public override string[] Aliases { get; } = { "katakanya" }; 18 | 19 | public override string Description => "カタカナをランダムに出します"; 20 | 21 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 22 | { 23 | var count = 3; 24 | if (args.Length > 0 && !int.TryParse(args[0], out count)) 25 | throw new CommandException(); 26 | return string.Concat(Enumerable.Repeat(0, count).Select(_ => katakana.Random())); 27 | } 28 | 29 | readonly char[] katakana = "アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヰヱヲンヴガギグゲゴザジズゼゾダヂヅデドバビブベボパポプペポァィゥェォャュョヮッヵヶ".ToArray(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Commands/HiraganaCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core 10 | { 11 | public class HiraganaCommand : CommandBase 12 | { 13 | public override string Name => "hiragana"; 14 | 15 | public override string Usage => "/hiragana "; 16 | 17 | public override string[] Aliases { get; } = { "kana", "hiraganya", "kanya" }; 18 | 19 | public override string Description => "ひらがなをランダムに出します"; 20 | 21 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 22 | { 23 | var count = 3; 24 | if (args.Length > 0 && !int.TryParse(args[0], out count)) 25 | throw new CommandException(); 26 | return string.Concat(Enumerable.Repeat(0, count).Select(_ => hiragana.Random())); 27 | } 28 | 29 | readonly char[] hiragana = "あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわゐゑをんゔがぎぐげござじずぜぞだぢづでどばびぶべぼぱぽぷぺぽぁぃぅぇぉゃゅょゎっ".ToArray(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Commands/DumpCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | using BotBone.Core.Modules; 7 | 8 | namespace Citrine.Core 9 | { 10 | public class DumpCommand : CommandBase 11 | { 12 | public override string Name => "dump"; 13 | 14 | public override string Usage => "/dump"; 15 | 16 | public override string Description => "このコマンドを含む投稿の情報を、アプリケーションの標準入出力に表示します。"; 17 | 18 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 19 | { 20 | if (sender is not PostCommandSender p) 21 | return "このコマンドはユーザーが実行してください."; 22 | var n = p.Post; 23 | 24 | logger.Info($@"Dumped Post 25 | id: {n.Id} 26 | name: {n.User.Name ?? "NULL"} 27 | host: {n.User.Host ?? "NULL"} 28 | screenName: {n.User.ScreenName ?? "NULL"} 29 | text: {n.Text ?? "NULL"} 30 | visibility: {n.Visiblity}"); 31 | return "この投稿をコンソールに出力しました. コンソール画面を確認してください."; 32 | } 33 | 34 | private readonly Logger logger = new Logger(nameof(DumpCommand)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.vscode/solution-explorer/template-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "templates": [ 3 | { 4 | "name": "Class", 5 | "extension": "cs", 6 | "file": "./class.cs-template", 7 | "parameters": "./template-parameters.js" 8 | }, 9 | { 10 | "name": "Interface", 11 | "extension": "cs", 12 | "file": "./interface.cs-template", 13 | "parameters": "./template-parameters.js" 14 | }, 15 | { 16 | "name": "Enum", 17 | "extension": "cs", 18 | "file": "./enum.cs-template", 19 | "parameters": "./template-parameters.js" 20 | }, 21 | { 22 | "name": "Class", 23 | "extension": "ts", 24 | "file": "./class.ts-template", 25 | "parameters": "./template-parameters.js" 26 | }, 27 | { 28 | "name": "Interface", 29 | "extension": "ts", 30 | "file": "./interface.ts-template", 31 | "parameters": "./template-parameters.js" 32 | }, 33 | { 34 | "name": "Default", 35 | "extension": "ts", 36 | "file": "./default.ts-template", 37 | "parameters": "./template-parameters.js" 38 | }, 39 | { 40 | "name": "Class", 41 | "extension": "vb", 42 | "file": "./class.vb-template", 43 | "parameters": "./template-parameters.js" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /Commands/TranslateCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | using BotBone.Core.Modules; 7 | 8 | namespace Citrine.Core 9 | { 10 | public class TranslateCommand : CommandBase 11 | { 12 | public override string Name => "translate"; 13 | 14 | public override string Usage => "/translate "; 15 | 16 | public override string Description => "テキストを翻訳します。"; 17 | 18 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 19 | { 20 | if (args.Length < 3) 21 | throw new CommandException(); 22 | var from = args[0].ToLowerInvariant(); 23 | if (from == "auto") from = ""; 24 | var to = args[1].ToLowerInvariant(); 25 | var text = string.Join(" ", args.Skip(2)); 26 | 27 | var url = $"https://script.google.com/macros/s/AKfycbzBORthiOILTTAOHd778LawGkjp5Lii7p2ttaMADMHSDHuUS3M/exec?text={text}&source={from}&target={to}"; 28 | return await (await Server.Http.GetAsync(url)).Content.ReadAsStringAsync(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Modules/CallMeModule.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core.Modules 8 | { 9 | public class CallMeModule : ModuleBase 10 | { 11 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 12 | { 13 | if (n.Text == null) 14 | return false; 15 | var m = Regex.Match(n.Text.TrimMentions(), @"(.+)(って|と)呼[べびん]"); 16 | if (m.Success) 17 | { 18 | switch (core.GetRatingOf(n.User)) 19 | { 20 | case Rating.Hate: 21 | await shell.ReplyAsync(n, "..."); 22 | break; 23 | case Rating.Normal: 24 | await shell.ReplyAsync(n, "もう少し仲良くなってから,もう一度お願いしてね"); 25 | break; 26 | default: 27 | var nick = m.Groups[1].Value; 28 | core.SetNicknameOf(n.User, nick); 29 | EconomyModule.Pay(n, shell, core); 30 | core.LikeWithLimited(n.User); 31 | await shell.ReplyAsync(n, $"わかった.これからは{core.GetNicknameOf(n.User)}と呼ぶね.\nよろしくね,{core.GetNicknameOf(n.User)}"); 32 | break; 33 | } 34 | return true; 35 | } 36 | return false; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Modules/ImHereModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core.Modules 8 | { 9 | public class ImHereModule : ModuleBase 10 | { 11 | public static readonly string StatImHereCount = "stat.im-here-count"; 12 | public override async Task OnTimelineAsync(IPost n, IShell shell, Server core) 13 | { 14 | if (n.Text != null && n.Text.TrimMentions().ToHiragana().IsMatch(@"^しとりん(ちゃん|さん|様)?(何処|どこ|[居い](ますか|る[\??]))")) 15 | { 16 | // 友好度が低ければやらない 17 | if (core.GetRatingOf(n.User) < Rating.Like) 18 | return false; 19 | 20 | // 遊び時間 21 | await Task.Delay(3000 + rnd.Next(4000)); 22 | await shell.ReactAsync(n, "❤️"); 23 | 24 | await Task.Delay(250); 25 | core.Storage[n.User].Add(StatImHereCount); 26 | await shell.ReplyAsync(n, patterns.Random(rnd).Replace("{user}", core.GetNicknameOf(n.User))); 27 | } 28 | return false; 29 | } 30 | private readonly Random rnd = new Random(); 31 | 32 | private readonly string[] patterns = 33 | { 34 | "ここだよー", 35 | "ここにいるよ", 36 | "ここです!", 37 | "どうしたの {user}.", 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019-2021 Xeltica and GitHub Contributors 4 | 5 | Part of FortuneModule.strings.cs: Copyright (c) 2018 syuilo 6 | 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /Commands/OjisanCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | using Newtonsoft.Json; 9 | 10 | namespace Citrine.Core 11 | { 12 | public class OjisanCommand : CommandBase 13 | { 14 | public override string Name => "ojisan"; 15 | 16 | public override string Usage => "/ojisan [name]"; 17 | 18 | public override string Description => "おじさん構文を返します。"; 19 | 20 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 21 | { 22 | var req = !string.IsNullOrEmpty(body) ? new FormUrlEncodedContent(new[]{ 23 | new KeyValuePair("name", body) 24 | }) : new StringContent("") as HttpContent; 25 | var res = await (await Server.Http.PostAsync("https://ojichat.appspot.com/post", req)).Content.ReadAsStringAsync(); 26 | return JsonConvert.DeserializeObject(res).Message ?? ""; 27 | } 28 | 29 | class OjichatResponse 30 | { 31 | [JsonProperty("message")] 32 | public string? Message { get; set; } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Commands/PipeCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core 10 | { 11 | public class PipeCommand : CommandBase 12 | { 13 | public override string Name => "pipe"; 14 | 15 | public override string Usage => "/pipe (これ以降にコマンドを | で区切って並べます。)"; 16 | 17 | public override string Description => "各種コマンドを連結して実行します。前のコマンドの出力は、次のコマンドの末尾に追記されます。"; 18 | 19 | public override Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 20 | { 21 | return RunPipeAsync(sender, core, body, '|'); 22 | } 23 | 24 | public static async Task RunPipeAsync(ICommandSender sender, Server core, string body, params char[] split) 25 | { 26 | var lines = body.Replace("\r", "\n").Replace("\r\n", "\n").Split(split).Where(l => !string.IsNullOrWhiteSpace(l)).Select(l => l.Trim()); 27 | var output = ""; 28 | foreach (var line in lines) 29 | { 30 | output = await core.ExecCommand(sender, line + " " + output); 31 | output = output.Trim(); 32 | } 33 | return output; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Modules/WeatherModule.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | using BotBone.Core; 9 | using BotBone.Core.Api; 10 | using BotBone.Core.Modules; 11 | using Newtonsoft.Json; 12 | 13 | namespace Citrine.Core.Modules 14 | { 15 | public class WeatherModule : ModuleBase 16 | { 17 | public override int Priority => -9000; 18 | 19 | public static readonly string StatForecastCount = "stat.forecast-count"; 20 | 21 | public async override Task ActivateAsync(IPost n, IShell shell, Server core) 22 | { 23 | var req = n.Text?.TrimMentions(); 24 | if (string.IsNullOrEmpty(req)) 25 | return false; 26 | 27 | var m = Regex.Match(req, "(.+)の天気"); 28 | if (m.Success) 29 | { 30 | await shell.ReplyAsync(n, sorryMessage.Random()); 31 | return true; 32 | } 33 | return false; 34 | } 35 | 36 | private readonly string[] sorryMessage = 37 | { 38 | "ごめん,天気予報マシンが壊れちゃって. 修理中なので少し待っててほしい", 39 | "ごめんだけど,天気予報マシンが動かなくなっちゃったので,今は答えられないの", 40 | "残念だけど,天気予報マシンが故障して使えなくなっちゃったの. 修理後にまた聞いてくれる?", 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Modules/EraitModule.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core.Modules 8 | { 9 | public class EraitModule : ModuleBase 10 | { 11 | public static readonly string StatEraitedCount = "stat.eraited-count"; 12 | 13 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 14 | { 15 | if (n.Text == null) 16 | return false; 17 | if (core.GetRatingOf(n.User) == Rating.Hate) 18 | return false; 19 | await Task.Delay(4000); 20 | var reg = Regex.Match(n.Text.TrimMentions(), @"(.+)(から褒めて|の(えら|偉)い)"); 21 | var reg2 = Regex.Match(n.Text.TrimMentions(), @"褒めて|(えら|偉)い\??"); 22 | if (reg.Success) 23 | { 24 | await shell.ReplyAsync(n, $"{reg.Groups[1].Value}のえらいです"); 25 | core.Storage[n.User].Add(StatEraitedCount); 26 | core.LikeWithLimited(n.User); 27 | EconomyModule.Pay(n, shell, core); 28 | return true; 29 | } 30 | else if (reg2.Success) 31 | { 32 | await shell.ReplyAsync(n, "えらいです"); 33 | core.Storage[n.User].Add(StatEraitedCount); 34 | EconomyModule.Pay(n, shell, core); 35 | core.LikeWithLimited(n.User); 36 | return true; 37 | } 38 | return false; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Modules/Markov/MarkovNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Citrine.Core.Modules.Markov 5 | { 6 | public class MarkovNode 7 | { 8 | public string Value { get; set; } = ""; 9 | 10 | public List Children { get; set; } = new List(); 11 | 12 | public DateTime CreatedAt { get; set; } = DateTime.Now; 13 | 14 | public MarkovNode() { } 15 | 16 | public MarkovNode(string value) => Value = value; 17 | 18 | public static MarkovNode Start => new MarkovNode("START OF NODE"); 19 | 20 | public static MarkovNode End { get; } = new MarkovNode("END OF NODE") 21 | { 22 | CreatedAt = default, 23 | Children = new List(), 24 | }; 25 | 26 | public static bool operator ==(MarkovNode? n1, MarkovNode? n2) => n1?.Value == n2?.Value; 27 | public static bool operator !=(MarkovNode? n1, MarkovNode? n2) => n1?.Value != n2?.Value; 28 | 29 | public override bool Equals(object obj) 30 | { 31 | return obj is MarkovNode node && 32 | Value == node.Value && 33 | EqualityComparer>.Default.Equals(Children, node.Children) && 34 | CreatedAt == node.CreatedAt; 35 | } 36 | 37 | public override int GetHashCode() 38 | { 39 | return HashCode.Combine(Value, Children, CreatedAt); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Commands/RatingModule.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core 8 | { 9 | public class RatingCommand : CommandBase 10 | { 11 | public override string Name => "rating"; 12 | 13 | public override string Usage => "/rating "; 14 | 15 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 16 | { 17 | if(sender is not PostCommandSender p) 18 | return "use from post"; 19 | if (args.Length < 1) 20 | throw new CommandException(); 21 | 22 | switch (args[0].ToLowerInvariant().Trim()) 23 | { 24 | case "set": 25 | if (!sender.IsAdmin) 26 | throw new AdminOnlyException(); 27 | core.SetRatingValueOf(p.User.Id, int.Parse(args[1])); 28 | break; 29 | case "add": 30 | if (!sender.IsAdmin) 31 | throw new AdminOnlyException(); 32 | core.Like(p.User.Id, int.Parse(args[1])); 33 | break; 34 | case "get": 35 | return core.GetRatingValueOf(p.User.Id).ToString(); 36 | case "status": 37 | return core.GetRatingOf(p.User.Id).ToString(); 38 | } 39 | return "ok"; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build-misskey", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Citrine.Misskey/Citrine.Misskey.csproj" 11 | ], 12 | "problemMatcher": "$msCompile" 13 | }, 14 | { 15 | "label": "build-mastodon", 16 | "command": "dotnet", 17 | "type": "process", 18 | "args": [ 19 | "build", 20 | "${workspaceFolder}/Citrine.Mastodon/Citrine.Mastodon.csproj" 21 | ], 22 | "problemMatcher": "$msCompile" 23 | }, 24 | { 25 | "label": "build-discord", 26 | "command": "dotnet", 27 | "type": "process", 28 | "args": [ 29 | "build", 30 | "${workspaceFolder}/Citrine.Discord/Citrine.Discord.csproj" 31 | ], 32 | "problemMatcher": "$msCompile" 33 | }, 34 | { 35 | "label": "build-standalone", 36 | "command": "dotnet", 37 | "type": "process", 38 | "args": [ 39 | "build", 40 | "${workspaceFolder}/Citrine.Standalone/Citrine.Standalone.csproj" 41 | ], 42 | "problemMatcher": "$msCompile" 43 | }, 44 | { 45 | "label": "build-sea", 46 | "command": "dotnet", 47 | "type": "process", 48 | "args": [ 49 | "build", 50 | "${workspaceFolder}/Citrine.Sea/Citrine.Sea.csproj" 51 | ], 52 | "problemMatcher": "$msCompile" 53 | } 54 | ] 55 | } -------------------------------------------------------------------------------- /Commands/NmoudameCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System; 3 | using System.Threading.Tasks; 4 | using System.Web; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | using Newtonsoft.Json.Linq; 9 | 10 | namespace Citrine.Core 11 | { 12 | public class NmoudameCommand : CommandBase 13 | { 14 | public override string Name => "nmoudame"; 15 | 16 | public override string Usage => "/nmoudame"; 17 | 18 | public override string[] Aliases => new[] { "nmudm" }; 19 | 20 | public override string Description => "テェエッキイ…ィ!ス…ウゥットオ…ォッ!を…ぉぉっ変…っ換……!し…ぃいぃ!て………っ返……し…ぃいい…まあ…っ!す…ぅっ!。"; 21 | 22 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 23 | { 24 | try 25 | { 26 | return JObject.Parse(await (await Server.Http.GetAsync(GetEndpoint(body))).Content.ReadAsStringAsync())["result"].Value(); 27 | } 28 | catch (Exception ex) 29 | { 30 | logger.Error($"{ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}"); 31 | return "エラー"; 32 | } 33 | } 34 | private readonly Logger logger = new Logger(nameof(NmoudameCommand)); 35 | 36 | private static string GetEndpoint(string text) => $"http://tekito.kanichat.com/nmoudame/response.php?str={HttpUtility.UrlEncode(text)}&mode=json&length=6&normal=1&small=1&dots=1<u_prob=30&ex_prob=30&c=0"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Commands/WordCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using Citrine.Core.Modules; 8 | 9 | namespace Citrine.Core 10 | { 11 | using static FortuneModule; 12 | using static FortuneExtension; 13 | public class WordCommand : CommandBase 14 | { 15 | public override string Name => "word"; 16 | 17 | public override string Usage => "/word"; 18 | 19 | public override string Description => "ランダムな言葉を生成します。"; 20 | 21 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 22 | { 23 | if (args.Length > 0) 24 | { 25 | if (args[0].ToLowerInvariant() == "total") 26 | { 27 | return (ItemPrefixes.Length * ItemSuffixes.Length * Items.Length + 28 | ItemPrefixes.Length * Items.Length + 29 | Items.Length).ToString(); 30 | } 31 | else if (int.TryParse(args[0], out var length) && length > 0) 32 | { 33 | var sb = new StringBuilder(); 34 | length = Math.Min(length, 100); 35 | for (var i = 0; i < length; i++) 36 | { 37 | sb.AppendLine(GenerateWord()); 38 | } 39 | return sb.ToString(); 40 | } 41 | else 42 | { 43 | throw new CommandException(); 44 | } 45 | } 46 | return GenerateWord(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Modules/FollowBackModule.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core.Modules 8 | { 9 | public class FollowBackModule : ModuleBase 10 | { 11 | public override async Task OnFollowedAsync(IUser user, IShell shell, Server core) 12 | { 13 | if (core.GetRatingOf(user) == Rating.Hate) 14 | return true; 15 | core.LikeWithLimited(user); 16 | await shell.FollowAsync(user); 17 | return false; 18 | } 19 | 20 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 21 | { 22 | if (n.Text == null) return false; 23 | if (n.Text.IsMatch("フォロ[ーバ](バック)?し")) 24 | { 25 | if (core.GetRatingOf(n.User) == Rating.Hate) 26 | return true; 27 | core.LikeWithLimited(n.User); 28 | await shell.FollowAsync(n.User); 29 | await shell.ReactAsync(n, "✌️"); 30 | return true; 31 | } 32 | if (n.Text.IsMatch("フォロ[ーバ](バック)?(解除|外し|[や辞]め)")) 33 | { 34 | core.LikeWithLimited(n.User); 35 | await shell.UnfollowAsync(n.User); 36 | await shell.ReactAsync(n, "👋"); 37 | return true; 38 | } 39 | return false; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Modules/CommandModule.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using BotBone.Core; 3 | using BotBone.Core.Api; 4 | using BotBone.Core.Modules; 5 | 6 | namespace Citrine.Core.Modules 7 | { 8 | public class CommandModule : ModuleBase 9 | { 10 | public override int Priority => -10000; 11 | public static readonly string StatCommandUsedCount = "stat.command.used-count"; 12 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 13 | { 14 | var t = n.Text?.TrimMentions(); 15 | if (string.IsNullOrEmpty(t)) 16 | return false; 17 | if (t.StartsWith("/")) 18 | { 19 | string response; 20 | try 21 | { 22 | response = await core.ExecCommand(new PostCommandSender(n, core.IsSuperUser(n.User)), t); 23 | core.Storage[n.User].Add(StatCommandUsedCount); 24 | } 25 | catch (AdminOnlyException) 26 | { 27 | response = "ごめん,そのコマンドは管理者以外に言われても実行するなと言われてるの"; 28 | } 29 | catch (LocalOnlyException) 30 | { 31 | response = "このコマンドは同じインスタンスのユーザーしか使えないよ.ごめんね."; 32 | } 33 | catch (RemoteOnlyException) 34 | { 35 | response = "このコマンドは違うインスタンスのユーザーしか使えないよ.ごめんね."; 36 | } 37 | catch (NoSuchCommandException) 38 | { 39 | response = $"No such command."; 40 | } 41 | 42 | if (!string.IsNullOrWhiteSpace(response)) 43 | { 44 | await shell.ReplyAsync(n, response); 45 | } 46 | EconomyModule.Pay(n, shell, core); 47 | return true; 48 | } 49 | return false; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Modules/IntegrationFamilyModule.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | using BotBone.Core.Modules; 7 | 8 | namespace Citrine.Core 9 | { 10 | public class IntegrationFamilyModule : ModuleBase 11 | { 12 | public static readonly string CitrineIntegrationInteractedNoteIds = "citrine.integration.interactedNoteIds"; 13 | // 反応する単語。完全一致 14 | public static readonly string[] Keywords = { 15 | "アカウント作りました!よろしくね", 16 | }; 17 | 18 | public override async Task OnTimelineAsync(IPost n, IShell shell, Server core) 19 | { 20 | var s = core.GetMyStorage(); 21 | var list = s.Get(CitrineIntegrationInteractedNoteIds, new List()); 22 | var post = n.Repost is not null ? n.Repost : n; 23 | 24 | // 既に反応していれば関わらない 25 | if (list.Contains(post.Id)) return false; 26 | // テキスト一致判定 27 | var isMatch = Keywords.Contains(post.Text?.Trim()); 28 | // うちの子ファミリーのbotであるかどうかの雑判定 29 | // 名前一致と同一ホストかどうかの判定 30 | var isFamily = post.User.Name.ToLowerInvariant() == "kaho" && string.IsNullOrEmpty(post.User.Host); 31 | if (isMatch && isFamily) 32 | { 33 | // 既に反応したノートとしてリストアップする 34 | list.Add(post.Id); 35 | s.Set(CitrineIntegrationInteractedNoteIds, list); 36 | await Task.Delay(1200); 37 | 38 | await shell.RepostAsync(post); 39 | await Task.Delay(800); 40 | await shell.FollowAsync(post.User); 41 | } 42 | return false; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Modules/BackupModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | using System.Timers; 8 | using BotBone.Core; 9 | using BotBone.Core.Api; 10 | using BotBone.Core.Modules; 11 | 12 | namespace Citrine.Core.Modules 13 | { 14 | public class BackupModule : ModuleBase 15 | { 16 | public BackupModule() 17 | { 18 | timer = new Timer(1000 * 60 * 30) 19 | { 20 | AutoReset = true, 21 | Enabled = true, 22 | }; 23 | timer.Elapsed += OnTick; 24 | } 25 | 26 | public override async Task OnTimelineAsync(IPost n, IShell shell, Server core) 27 | { 28 | (this.core, this.shell) = (core, shell); 29 | await Task.Delay(0); 30 | return false; 31 | } 32 | 33 | private async void OnTick(object sender, ElapsedEventArgs e) 34 | { 35 | if (core == null || shell == null) 36 | return; 37 | logger.Info("Start backup for storage.json"); 38 | if (!Directory.Exists("backup")) 39 | { 40 | Directory.CreateDirectory("backup"); 41 | } 42 | var storage = await File.ReadAllTextAsync("storage.json"); 43 | var n = DateTime.Now; 44 | await File.WriteAllTextAsync($"backup/storage-{n.Year}-{n.Month}-{n.Day}-{n.Hour}-{n.Minute}-{n.Second}-{n.Millisecond}.json", storage); 45 | logger.Info("Saved a backup for storage.json"); 46 | } 47 | 48 | private readonly Timer timer; 49 | private Server? core; 50 | private IShell? shell; 51 | 52 | private readonly Logger logger = new Logger(nameof(BackupModule)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Commands/DiceCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core 10 | { 11 | public class DiceCommand : CommandBase 12 | { 13 | public override string Name => "dice"; 14 | 15 | public override string Usage => @"/dice [times] [max] 16 | /dice [times]d"; 17 | 18 | public override string Description => "サイコロを振ります。通常の引数指定か、ダイスロール記法で指定します。省略時は1d6に相当。"; 19 | 20 | public static readonly Random Rand = new Random(); 21 | 22 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 23 | { 24 | Assert(args.Length <= 2); 25 | var (times, max) = (1, 6); 26 | if (args.Length == 2) 27 | { 28 | Assert(int.TryParse(args[0], out times)); 29 | Assert(int.TryParse(args[1], out max)); 30 | } 31 | else if (args.Length == 1) 32 | { 33 | if (args[0].ToLowerInvariant().Contains("d")) 34 | { 35 | var d = args[0].ToLowerInvariant().Split('d'); 36 | Assert(d.Length == 2); 37 | Assert(int.TryParse(d[0] != "" ? d[0] : "1", out times)); 38 | Assert(int.TryParse(d[1], out max)); 39 | } 40 | else 41 | { 42 | Assert(int.TryParse(args[0], out max)); 43 | } 44 | } 45 | times = times > 0 ? times : 1; 46 | max = max > 0 ? max : 6; 47 | return string.Join(" ", Enumerable.Repeat(0, times).Select(_ => Rand.Next(max) + 1)); 48 | } 49 | 50 | private void Assert(bool condition) 51 | { 52 | if (!condition) 53 | { 54 | throw new CommandException(); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Commands/HelpCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using BotBone.Core; 7 | using BotBone.Core.Api; 8 | using BotBone.Core.Modules; 9 | 10 | namespace Citrine.Core 11 | { 12 | public class HelpCommand : CommandBase 13 | { 14 | public override string Name => "help"; 15 | 16 | public override string Usage => "/help [name]"; 17 | 18 | public override string Description => "コマンドのヘルプを表示します。"; 19 | 20 | public override string[] Aliases { get; } = { "h" }; 21 | 22 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 23 | { 24 | if (args.Length == 0) 25 | { 26 | var descriptions = core.Commands.Select(GetDescription); 27 | 28 | return string.Join("\n", descriptions); 29 | } 30 | else 31 | { 32 | var name = args[0]; 33 | var cmd = core.TryGetCommand(name); 34 | if (cmd == null) 35 | return $"コマンド {name} は見つかりませんでした."; 36 | var sb = new StringBuilder(); 37 | sb.AppendLine(GetDescription(cmd)); 38 | sb.Append("使い方: "); 39 | sb.AppendLine(cmd.Usage); 40 | if (cmd.Aliases != null) 41 | sb.AppendLine(string.Join(", ", cmd.Aliases)); 42 | sb.AppendLine(DumpPermission(cmd.Permission)); 43 | 44 | return sb.ToString(); 45 | } 46 | } 47 | 48 | private string GetDescription(ICommand cmd) => $"/{cmd.Name} - {cmd.Description ?? "説明はありません。"}"; 49 | 50 | private string DumpPermission(PermissionFlag flag) 51 | { 52 | var sb = new StringBuilder(); 53 | if (flag.HasFlag(PermissionFlag.AdminOnly)) 54 | sb.Append("(管理者限定)"); 55 | if (flag.HasFlag(PermissionFlag.LocalOnly)) 56 | sb.Append("(ローカルユーザー限定)"); 57 | return sb.ToString(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Commands/BinCommand.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable CS1998 // 非同期メソッドは、'await' 演算子がないため、同期的に実行されます 2 | using System.Linq; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core 10 | { 11 | public class BinCommand : CommandBase 12 | { 13 | public override string Name => "tobyte"; 14 | 15 | public override string Usage => "/tobyte "; 16 | 17 | public override string Description => "テキストをバイナリダンプします。"; 18 | 19 | public override string[] Aliases => new[] { "bin" }; 20 | 21 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 22 | { 23 | var sb = new StringBuilder(); 24 | var cur = 0; 25 | foreach (var ch in Encoding.UTF8.GetBytes(body)) 26 | { 27 | sb.Append($"{ch:x2} "); 28 | cur++; 29 | if (cur == 16) 30 | { 31 | cur = 0; 32 | sb.AppendLine(); 33 | } 34 | } 35 | return sb.ToString(); 36 | } 37 | } 38 | 39 | public class RepeatCommand : CommandBase 40 | { 41 | public override string Name => "repeat"; 42 | 43 | public override string Usage => "/repeat , "; 44 | 45 | public override string Description => "コマンドを繰り返し実行し、結果を改行区切りで返します"; 46 | 47 | public override async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 48 | { 49 | if (args.Length < 2) throw new CommandException(); 50 | var count = int.TryParse(args[0], out var c) ? c : throw new CommandException(); 51 | var cmd = string.Join(' ', args.Skip(1)); 52 | var output = ""; 53 | for (var _ = 0; _ < count; _++) 54 | { 55 | output += await core.ExecCommand(InternalCommandSender.Instance, cmd) + "\n"; 56 | } 57 | return output; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Modules/StatusModule.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Threading.Tasks; 3 | using BotBone.Core; 4 | using BotBone.Core.Api; 5 | using BotBone.Core.Modules; 6 | 7 | namespace Citrine.Core.Modules 8 | { 9 | public class StatusModule : ModuleBase 10 | { 11 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 12 | { 13 | var storage = core.Storage[n.User]; 14 | 15 | int StatOf(string key) => storage.Get(key, 0); 16 | 17 | static string GetRatingString(Rating r) => r switch 18 | { 19 | Rating.Partner => "大好き", 20 | Rating.BestFriend => "親友", 21 | Rating.Like => "仲良し", 22 | Rating.Normal => "普通", 23 | Rating.Hate => "キライ", 24 | _ => "不明", 25 | }; 26 | 27 | if (n.Text != null && n.Text.IsMatch("ステータスカード")) 28 | { 29 | var builder = new StringBuilder(); 30 | builder.AppendLine($"呼び名: {core.GetNicknameOf(n.User)}"); 31 | builder.AppendLine($"なかよし度: {GetRatingString(core.GetRatingOf(n.User))}"); 32 | builder.AppendLine($"所持金: {storage.Get("economy.balance", 0)}クォーツ"); 33 | builder.AppendLine($"占った回数: {StatOf(FortuneModule.StatFortuneCount)}回"); 34 | builder.AppendLine($"会話した回数: {StatOf(GreetingModule.StatTalkedCount)}回"); 35 | builder.AppendLine($"どこ〜って言われた回数: {StatOf(ImHereModule.StatImHereCount)}回"); 36 | builder.AppendLine($"じゃんけんで勝った回数: {StatOf(JankenModule.StatWinCount)}回"); 37 | builder.AppendLine($"じゃんけんで負けた回数: {StatOf(JankenModule.StatLoseCount)}回"); 38 | builder.AppendLine($"セクハラした回数: {StatOf(StorageKey.HarrasmentedCount)}回"); 39 | builder.AppendLine($"悪口を言った回数: {StatOf(ReactModule.StatBadMouthCount)}回"); 40 | builder.AppendLine($"寿司を握った回数: {StatOf(SushiModule.StatSushiCount)}回"); 41 | builder.AppendLine($"計算回数: {StatOf(SearchModule.StatCalculatedCount)}回"); 42 | builder.AppendLine($"言葉を調べた回数: {StatOf(SearchModule.StatSearchedCount)}回"); 43 | builder.AppendLine($"バレンタインチョコを貰った回数: {StatOf(ValentineModule.StatValentineCount)}回"); 44 | await shell.ReplyAsync(n, builder.ToString(), $"{core.GetNicknameOf(n.User)}のステータスカードを作ったよ"); 45 | return true; 46 | } 47 | return false; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Resources/ngwords.txt: -------------------------------------------------------------------------------- 1 | # コメントは # からはじまる 2 | # 大文字小文字、ひらがなカタカナ半角カナは区別されない 3 | # スペース文字(区切り文字)は取り除いて判定される 4 | # - を先頭につけた場合、例外を定義する。 5 | エッチ 6 | セックス 7 | セクロス 8 | -ぱちんこ 9 | -がちんこ 10 | -喉ちんこ 11 | -のどちんこ 12 | ちんこ 13 | ち○こ 14 | ちんぽ 15 | チンポ 16 | -ビックリマンコラボ 17 | -ウルトラマンコスモス 18 | まんこ 19 | ま○こ 20 | ペニス 21 | penis 22 | vagina 23 | ヴァギナ 24 | バギナ 25 | おめこ 26 | オメコ 27 | マソコ 28 | 肉棒 29 | 勃起 30 | ぼっき 31 | ボッキ 32 | 精子 33 | 精液 34 | 射精 35 | ザーメン 36 | ザー汁 37 | 金玉 38 | 赤ちゃん製造ミルク 39 | semen 40 | 体位 41 | 淫乱 42 | アナル 43 | anus 44 | おっぱい 45 | 巨乳 46 | 貧乳 47 | きょにゅう 48 | きょにゅー 49 | ひんにゅう 50 | ひんにゅー 51 | 何カップ 52 | ノーパン 53 | 乳首 54 | ちくび 55 | ビーチク 56 | 自慰 57 | オナニ 58 | オナ二 59 | オナヌ 60 | マスターベーション 61 | マスタベーション 62 | シコい 63 | シコっ 64 | エロい 65 | えちえち 66 | 脱げ 67 | ぬげ 68 | 脱いで 69 | ぬいで 70 | 脱ごう 71 | ぬごう 72 | 喘いで 73 | あえいで 74 | クンニ 75 | -フェラーリ 76 | -カフェラテ 77 | -フェライト 78 | -フェラガモ 79 | -フェラーラ 80 | -フェライニ 81 | -フェラーズ 82 | -フェラリア 83 | フェラ 84 | デリヘル 85 | -姦しい 86 | 姦 87 | 犯す 88 | やりまん 89 | ヤリマン 90 | やりちん 91 | ヤリチン 92 | パイパン 93 | ぱいぱん 94 | 中出し 95 | 中田氏 96 | 中で出 97 | スカトロ 98 | うんこ 99 | ウンコ 100 | -うんちく 101 | うんち 102 | -ウンチク 103 | ウンチ 104 | 手コキ 105 | 手マン 106 | 潮吹 107 | 下乳 108 | 横乳 109 | 指マン 110 | パイズリ 111 | ペェズリ 112 | -スレイプニル 113 | レイプ 114 | オフパコ 115 | パコる 116 | #理由: 同名のユーザーがMastodonにいるので 117 | -パコる君 118 | ドピュ 119 | ブリュ 120 | -ちんちん電車 121 | ちんちん 122 | ちんぽこ 123 | ぽこちん 124 | マン汁 125 | 膣 126 | 下の口 127 | コンドーム 128 | ホ別 129 | 騎乗位 130 | 正常位 131 | 座位 132 | ハメ撮り 133 | ちん毛 134 | まん毛 135 | 陰毛 136 | -インポート 137 | -インポッシブル 138 | -インポータ 139 | インポ 140 | 童貞もらって 141 | 童貞貰 142 | 童貞をもらって 143 | 童貞を貰 144 | ビンビン 145 | ケツの穴 146 | アナ、ゥ 147 | 糞を出 148 | 糞が出 149 | しゃぶれ 150 | いちもつ 151 | ウクライナ 152 | ロシア 153 | クリミア 154 | ベラルーシ 155 | 北朝鮮 156 | 中国 157 | ドネツク 158 | ルガンスク 159 | モスクワ 160 | russia 161 | ukraine 162 | crimea 163 | belarus 164 | korea 165 | china 166 | donetsk 167 | luhansk 168 | kyiv 169 | ウラジーミル 170 | プーチン 171 | ウォロディミル 172 | ゼレンスキー 173 | vladimir 174 | putin 175 | volodymyr 176 | zelenskyy 177 | 世界大戦 178 | ww2 179 | ww3 180 | nato 181 | 北大西洋条約機構 182 | 共和国 183 | belarus 184 | kgb 185 | ソ連 186 | ソビエト 187 | soviet 188 | ussr 189 | cccp 190 | チェルノブイリ 191 | chernobyl 192 | #邪淫 193 | 処女 194 | -処女作 195 | -処女の生血 196 | -処女の生き血 197 | 前立腺 198 | -------------------------------------------------------------------------------- /Modules/ValentineModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using System.Timers; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core.Modules 10 | { 11 | public class ValentineModule : ModuleBase 12 | { 13 | public static readonly string StatValentineCount = "stat.valentine-count"; 14 | 15 | public ValentineModule() 16 | { 17 | timer = new Timer(1000) 18 | { 19 | AutoReset = true, 20 | Enabled = true, 21 | }; 22 | timer.Elapsed += OnTick; 23 | } 24 | 25 | private async void OnTick(object sender, ElapsedEventArgs e) 26 | { 27 | if (core == null || shell == null) 28 | return; 29 | var t = DateTime.Today; 30 | 31 | if (prevDate.Day == t.Day) 32 | return; 33 | 34 | prevDate = t; 35 | 36 | if (IsValentineDay(t)) 37 | { 38 | var fans = core.Storage.Records.Keys.Where(id => core.GetRatingOf(id) >= Rating.BestFriend); 39 | foreach (var fan in fans) 40 | { 41 | var user = await shell.GetUserAsync(fan); 42 | if (user == null) 43 | continue; 44 | 45 | var storage = core.Storage[user]; 46 | if (storage.Get("lastValentineYear", 0) == t.Year) 47 | continue; 48 | 49 | var msg = $"{core.GetNicknameOf(user)}, ハッピーバレンタイン! 💝受け取ってほしいな."; 50 | await shell.SendDirectMessageAsync(user, msg); 51 | storage.Add(StatValentineCount); 52 | storage.Set("lastValentineYear", t.Year); 53 | } 54 | } 55 | } 56 | 57 | public async override Task OnTimelineAsync(IPost n, IShell shell, Server core) 58 | { 59 | (this.core, this.shell) = (core, shell); 60 | await Task.Delay(0); 61 | return false; 62 | } 63 | 64 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 65 | { 66 | if (IsValentineDay(DateTime.Today) && n.Text is string text) 67 | { 68 | if (text.IsMatch("(チョコ|ちょこ|🍫).*(あげる|どうぞ)") && core.GetRatingOf(n.User) >= Rating.Like) 69 | { 70 | await shell.ReplyAsync(n, thanksMessage.Random()); 71 | return true; 72 | } 73 | } 74 | return false; 75 | } 76 | 77 | public bool IsValentineDay(DateTime date) => date.Month == 2 && date.Day == 14; 78 | 79 | private readonly Timer timer; 80 | private DateTime prevDate; 81 | private Server? core; 82 | private IShell? shell; 83 | 84 | private readonly string[] thanksMessage = 85 | { 86 | "ありがと〜!", 87 | "わぁ!ありがとう!", 88 | "わぁ,ありがとう!", 89 | "私に!? ありがとう!", 90 | }; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Modules/SushiModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | using BotBone.Core.Modules; 7 | 8 | namespace Citrine.Core.Modules 9 | { 10 | public class SushiModule : ModuleBase 11 | { 12 | public static readonly string StatSushiCount = "stat.sushi-count"; 13 | 14 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 15 | { 16 | if (n.Text is string) 17 | { 18 | var m = Regex.Match(n.Text.TrimMentions(), "^(.+)(握|にぎ)"); 19 | if (m.Success) 20 | { 21 | var target = m.Groups[1].Value; 22 | if (target.Contains("寿司") | target.Contains("すし")) 23 | { 24 | var candidates = core.GetRatingOf(n.User) == Rating.Hate ? dusts : sushi; 25 | var res = ""; 26 | var s = random.Next(10) > 3 ? null : candidates.Random(); 27 | var max = random.Next(1, 10); 28 | for (var i = 0; i < max; i++) 29 | res += s ?? candidates.Random(); 30 | await shell.ReplyAsync(n, "ヘイお待ち! " + res); 31 | core.Storage[n.User].Add(StatSushiCount); 32 | EconomyModule.Pay(n, shell, core); 33 | core.LikeWithLimited(n.User); 34 | } 35 | else if (target.Length < 5) 36 | { 37 | await shell.ReplyAsync(n, messagesNigiri.Random().Replace("$user$", core.GetNicknameOf(n.User)).Replace("$thing$", target)); 38 | EconomyModule.Pay(n, shell, core); 39 | core.LikeWithLimited(n.User); 40 | } 41 | else 42 | { 43 | await shell.ReplyAsync(n, messagesReject.Random().Replace("$user$", core.GetNicknameOf(n.User)).Replace("$thing$", target)); 44 | EconomyModule.Pay(n, shell, core); 45 | core.LikeWithLimited(n.User); 46 | } 47 | return true; 48 | } 49 | } 50 | return false; 51 | } 52 | 53 | private readonly Random random = new Random(); 54 | 55 | private readonly string[] sushi = 56 | { 57 | "🍣", "🍣", "🍣", "🍣", "🍣", "🍣", "🍣", "🍣", "🍕", "🍔", "🍱", "🍘", "🍫", "📱", "💻", 58 | }; 59 | 60 | private readonly string[] dusts = 61 | { 62 | "🐛", "🍂", "🥦", "💩" 63 | }; 64 | 65 | private readonly string[] messagesNigiri = 66 | { 67 | "$user$の$thing$,握ったよ", 68 | "$user$の$thing$を握りました", 69 | "$user$の$thing$は私の物です🥴", 70 | }; 71 | 72 | private readonly string[] messagesReject = 73 | { 74 | "それはちょっと...", 75 | "それはさすがに無理かな", 76 | "ちょっとそれは厳しい", 77 | "厳しい", 78 | "無理です", 79 | "握れません", 80 | "$user$の$thing$は握るには難しい" 81 | }; 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Modules/HarassmentHandlerModule.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using System.Threading.Tasks; 6 | using BotBone.Core; 7 | using BotBone.Core.Api; 8 | using BotBone.Core.Modules; 9 | 10 | namespace Citrine.Core.Modules 11 | { 12 | /* === リプライ文字列の仕様 === 13 | * $user$ は相手のユーザー名, もしくはニックネームに置き換わる 14 | * $prefix$ はラッキーアイテムの修飾子辞書からランダムに取る 15 | * $item$ はラッキーアイテム辞書からランダムに取る 16 | * $rndA,B$はAからBまでの乱数 17 | */ 18 | public class HarassmentHandlerModule : ModuleBase 19 | { 20 | // コマンドよりも優先的 21 | public override int Priority => -10005; 22 | 23 | public List NgWords { get; } = new List(); 24 | 25 | public List ExcludedWords { get; } = new List(); 26 | 27 | public HarassmentHandlerModule() 28 | { 29 | using var reader = new StreamReader(GetType().Assembly.GetManifestResourceStream("Citrine.Resources.ngwords.txt")); 30 | while (!reader.EndOfStream) 31 | { 32 | var line = reader.ReadLine().Trim().ToLowerInvariant().ToHiragana(); 33 | if (line.StartsWith("#")) 34 | continue; 35 | if (line.StartsWith("-")) 36 | { 37 | ExcludedWords.Add(line[1..]); 38 | } 39 | else 40 | { 41 | NgWords.Add(line); 42 | } 43 | } 44 | } 45 | 46 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 47 | { 48 | if (n.Text == null) 49 | return false; 50 | 51 | if (IsHarassmented(n.Text)) 52 | { 53 | await shell.ReactAsync(n, reactions.Random()); 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | public override async Task OnTimelineAsync(IPost n, IShell shell, Server core) 60 | { 61 | await Task.Delay(0); 62 | // セクハラ投稿であれば見なかったことにする 63 | return n.Text is string t && IsHarassmented(t); 64 | } 65 | 66 | 67 | public bool IsHarassmented(string text) 68 | { 69 | text = Regex.Replace(text.TrimMentions(), @"[\s\.,//○●◯]", "").ToLowerInvariant().ToHiragana(); 70 | foreach (var w in ExcludedWords) 71 | text = text.Replace(w.ToHiragana(), ""); 72 | var m = ""; 73 | var res = NgWords.Any(w => 74 | { 75 | m = w; 76 | return text.Contains(w.ToHiragana()); 77 | }); 78 | if (res) 79 | { 80 | logger.Info($"Detected NG word '{m}' at the text '{text}'"); 81 | } 82 | return res; 83 | } 84 | 85 | private readonly string[] reactions = { 86 | "🥴", "🤔", "😇", "🤯" 87 | }; 88 | 89 | private readonly Logger logger = new Logger(nameof(HarassmentHandlerModule)); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /Modules/ReactModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | using Citrine.Core.Modules.Markov; 9 | 10 | namespace Citrine.Core.Modules 11 | { 12 | public class ReactModule : ModuleBase 13 | { 14 | public override int Priority => -1000; 15 | 16 | public static readonly string StatReactCount = "stat.react-count"; 17 | 18 | public static readonly string StatBadMouthCount = "stat.bad-mouth-count"; 19 | 20 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 21 | { 22 | if (n.User.Id == shell.Myself?.Id) 23 | return false; 24 | 25 | if (n.Text == null) 26 | return false; 27 | 28 | var m = Regex.Match(n.Text, "((?:\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]|[\\:a-z0-9A-Z_]+))を?[投な]げ[てろよ]"); 29 | if (m.Success) 30 | { 31 | core.LikeWithLimited(n.User); 32 | EconomyModule.Pay(n, shell, core); 33 | await shell.ReactAsync(n, m.Groups[1].Value.Trim()); 34 | core.Storage[n.User].Add(StatReactCount); 35 | return true; 36 | } 37 | else if (IsTerribleText(n.Text)) 38 | { 39 | core.OnHarassment(n.User); 40 | await shell.ReactAsync(n, "😥"); 41 | core.Storage[n.User].Add(StatBadMouthCount); 42 | var rate = core.GetRatingOf(n.User); 43 | await shell.ReplyAsync(n, (rate == Rating.Hate ? ponkotsuPatternHate : rate == Rating.Normal ? ponkotsuPattern : ponkotsuPatternLove).Random()); 44 | return true; 45 | } 46 | 47 | return false; 48 | } 49 | 50 | public override async Task OnTimelineAsync(IPost n, IShell shell, Server core) 51 | { 52 | await Task.Delay(0); 53 | if (string.IsNullOrEmpty(n.Text)) 54 | return false; 55 | 56 | // ひどい言葉は見て見ぬ振り 57 | if (IsTerribleText(n.Text)) 58 | return true; 59 | return false; 60 | } 61 | 62 | private bool IsTerribleText(string text) 63 | { 64 | var token = TinySegmenter.Instance.Segment(text).Select(t => t.ToHiragana()).ToArray(); 65 | var detected = token.Any(dic.Contains); 66 | return detected && !text.IsMatch("(じゃ|では?)な[いく]"); 67 | } 68 | 69 | private static readonly string[] dic = { 70 | "ぽんこつ", 71 | "馬鹿", 72 | "ばか", 73 | "あほ", 74 | "阿呆", 75 | "間抜け", 76 | "まぬけ", 77 | "ごみ", 78 | "死ね", 79 | "ぶす", 80 | "ぶさいく", 81 | "不細工", 82 | "無能", 83 | "きもい", 84 | "殺す", 85 | }; 86 | 87 | private static readonly string[] ponkotsuPattern = 88 | { 89 | "酷いです...", 90 | "ひどい...", 91 | "なんでそういうこと言うんですか.", 92 | "そういう言葉嫌いです", 93 | "そういう言葉遣い,とても嫌です", 94 | "そういう言葉遣い,嫌です", 95 | "そんなこと言われると傷つきます", 96 | "..." 97 | }; 98 | 99 | private static readonly string[] ponkotsuPatternHate = 100 | { 101 | "本当に最低だね", 102 | "は?", 103 | "何なんですか?", 104 | "いい加減にしてください。", 105 | "どこまで私を侮蔑すれば気が済むの?", 106 | "最低", 107 | "..." 108 | }; 109 | 110 | private static readonly string[] ponkotsuPatternLove = 111 | { 112 | "ひどい", 113 | "え,何でそういうこと言うの?", 114 | "嫌いになったの...?", 115 | "ひどいよ...", 116 | "あんまりそういうこと言われると嫌いになってしまいますよ.", 117 | "..." 118 | }; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Modules/UserMovingKit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using BotBone.Core; 6 | using BotBone.Core.Api; 7 | using BotBone.Core.Modules; 8 | 9 | namespace Citrine.Core.Modules 10 | { 11 | public class UserMovingKit : ModuleBase, ICommand 12 | { 13 | public string Name => "moving-code"; 14 | 15 | public string[] Aliases => new string[0]; 16 | 17 | public bool IgnoreCase => true; 18 | 19 | public PermissionFlag Permission => PermissionFlag.Any; 20 | 21 | public string Usage => "/moving-code "; 22 | 23 | public string Description => "引っ越しコードを入力して、他アカウントからのお引越しを行います。"; 24 | 25 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 26 | { 27 | if (n.Text == null) 28 | return false; 29 | 30 | string? message = null; 31 | 32 | if (n.Text.IsMatch("(引っ?越|ひっこ)しし?たい")) 33 | { 34 | if (movingCodesSet.FirstOrDefault(m => m.userId == n.User.Id) is (string userId, string movingCode) set) 35 | { 36 | // 既にコードがあればそれを返す 37 | message = $"ん?,あなたには既に引っ越し手続きコードを発行しているね. 引っ越ししたいアカウントで私に、リプライで次のようなコマンドを送ってね.\n/moving-code {set.movingCode}"; 38 | } 39 | else 40 | { 41 | // 10桁のIDを発行 42 | var code = GenerateCode(10); 43 | 44 | while (movingCodesSet.Any(c => c.movingCode == code)) 45 | { 46 | // コードが被ってしまったら再生成する 47 | code = GenerateCode(10); 48 | } 49 | movingCodesSet.Add((n.User.Id, code)); 50 | message = $"はい,引っ越し手続きコードを発行したよ. 引っ越ししたいアカウントで私に、リプライで次のようなコマンドを送ってね.\n/moving-code {code}"; 51 | } 52 | } 53 | else if (n.Text.IsMatch("(引っ?越|ひっこ)しキャンセル")) 54 | { 55 | if (!(movingCodesSet.FirstOrDefault(m => m.userId == n.User.Id) is (string userId, string movingCode) set)) 56 | { 57 | message = "ん...? あなたは元々引っ越し手続きをしていないみたいだよ? 引っ越しをしたかったら,引っ越ししたい って言ってね."; 58 | } 59 | else 60 | { 61 | movingCodesSet.RemoveAll(r => r.userId == n.User.Id); 62 | message = "わかった. 引っ越し手続きはキャンセルしたよ. またしたくなったら声かけてね."; 63 | } 64 | } 65 | if (message != null) 66 | { 67 | await shell.ReplyAsync(n, message, null, Visibility.Direct); 68 | return true; 69 | } 70 | return false; 71 | } 72 | 73 | public async Task OnActivatedAsync(ICommandSender sender, Server core, IShell shell, string[] args, string body) 74 | { 75 | if (body.ToLowerInvariant() == "dump") 76 | { 77 | if (!sender.IsAdmin) 78 | return "admin only"; 79 | var dumped = string.Join("\n", movingCodesSet.Select(s => $"{s.userId}: {s.movingCode}")); 80 | return string.IsNullOrEmpty(dumped) ? "no set" : dumped; 81 | } 82 | if (sender is not PostCommandSender s) 83 | { 84 | return "ユーザーから実行してください."; 85 | } 86 | 87 | if (!(movingCodesSet.FirstOrDefault(m => m.movingCode == body.Trim()) is (string userId, string movingCode) set)) 88 | { 89 | return "その引っ越し手続きコードは存在しないよ. コードが正しいかもう一度確認してね."; 90 | } 91 | 92 | core.Storage.Migrate(set.userId, s.User.Id); 93 | return "引っ越しが完了したよ〜. これからもよろしくね."; 94 | } 95 | 96 | private string GenerateCode(int length) 97 | { 98 | const string c = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 99 | return new string(Enumerable.Repeat('\0', length).Select(_ => c.Random()).ToArray()); 100 | } 101 | 102 | private readonly List<(string userId, string movingCode)> movingCodesSet = new List<(string userId, string movingCode)>(); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /Modules/JankenModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using BotBone.Core; 5 | using BotBone.Core.Api; 6 | using BotBone.Core.Modules; 7 | 8 | namespace Citrine.Core.Modules 9 | { 10 | public class JankenModule : ModuleBase 11 | { 12 | public static readonly string StatWinCount = "stat.janken.win-count"; 13 | public static readonly string StatLoseCount = "stat.janken.lose-count"; 14 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 15 | { 16 | if (n.Text != null && n.Text.Contains("じゃんけん")) 17 | { 18 | core.LikeWithLimited(n.User); 19 | var note = await shell.ReplyAsync(n, "負けませんよ.最初は✊,じゃんけん――"); 20 | if (note == null) 21 | return true; 22 | EconomyModule.Pay(n, shell, core); 23 | core.RegisterContext(note, this); 24 | return true; 25 | } 26 | 27 | return false; 28 | } 29 | 30 | public override async Task OnRepliedContextually(IPost n, IPost? context, Dictionary store, IShell shell, Server core) 31 | { 32 | if (n.Text == null) return false; 33 | var player = NormalizeHand(n.Text.TrimMentions()); 34 | if (player == null) 35 | { 36 | var r = await shell.ReplyAsync(n, "ちゃんと手を出してね.もしちゃんと出してるのにって思ったら,「グー,チョキ,パー」か,肌の色が黄色な手の絵文字であることを確認してね."); 37 | if (r == null) return true; 38 | core.RegisterContext(r, this); 39 | return true; 40 | } 41 | var me = new[] { "✊", "✌", "✋" }[rnd.Next(3)]; 42 | 43 | Result result = DoBSPGame(player, me); 44 | var output = result switch 45 | { 46 | Result.Draw => "あーいこで", 47 | Result.Win => "私の勝ちです!" + winMessage.Random().Replace("$user$", core.GetNicknameOf(n.User)), 48 | Result.Lose => $"私の負けです." + loseMessage.Random().Replace("$user$", core.GetNicknameOf(n.User)), 49 | _ => $"(Bug) Invalid State {result}", 50 | }; 51 | 52 | output = $"ポン! {me}\n{output}"; 53 | 54 | var storage = core.Storage[n.User]; 55 | 56 | if (result == Result.Win) 57 | storage.Add(StatWinCount); 58 | else if (result == Result.Lose) 59 | storage.Add(StatLoseCount); 60 | 61 | var replied = await shell.ReplyAsync(n, output); 62 | if (result == Result.Draw && replied != null) 63 | { 64 | core.RegisterContext(replied, this); 65 | } 66 | return true; 67 | } 68 | 69 | private static string? NormalizeHand(string text) 70 | { 71 | return text switch 72 | { 73 | "ちょき" => "✌", 74 | "チョキ" => "✌", 75 | "✌" => "✌", 76 | "グー" => "✊", 77 | "ぐー" => "✊", 78 | "✊" => "✊", 79 | "👊" => "✊", 80 | "パー" => "✋", 81 | "ぱー" => "✋", 82 | "✋" => "✋", 83 | "🤚" => "✋", 84 | "🖐" => "✋", 85 | _ => null, 86 | }; 87 | } 88 | 89 | private Result DoBSPGame(string player, string citrine) 90 | { 91 | return citrine == player ? Result.Draw 92 | : IsCitrinesWin(player, citrine) ? Result.Win 93 | : Result.Lose; 94 | } 95 | 96 | private bool IsCitrinesWin(string p, string c) => (c == "✋" && p == "✊") || (c == "✌" && p == "✋") || (c == "✊" && p == "✌"); 97 | 98 | private readonly Random rnd = new Random(); 99 | 100 | private readonly string[] loseMessage = { 101 | "$user$強いな.またやりましょ", 102 | "楽しかった.ありがとう!", 103 | "悔しい... 次は負けないよ.", 104 | "うーむ... 次こそは", 105 | "うーん,$user$...,泣きの一回はダメですか😢" 106 | }; 107 | 108 | private readonly string[] winMessage = { 109 | "楽しかった. ありがとう!", 110 | "$user$, 落ち込まないで...またやろ?", 111 | "またやりましょう👍" 112 | }; 113 | 114 | private enum Result 115 | { 116 | Win, 117 | Draw, 118 | Lose, 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Modules/GreetingModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | using BotBone.Core; 8 | using BotBone.Core.Api; 9 | using BotBone.Core.Modules; 10 | using Newtonsoft.Json; 11 | 12 | namespace Citrine.Core.Modules 13 | { 14 | /* === リプライ文字列の仕様 === 15 | * $user$ は相手のユーザー名もしくはニックネームに置き換わる 16 | * $prefix$ はラッキーアイテムの修飾子辞書からランダムに取る 17 | * $item$ はラッキーアイテム辞書からランダムに取る 18 | * $rndA,B$はAからBまでの乱数 19 | */ 20 | public partial class GreetingModule : ModuleBase, ICommand 21 | { 22 | public override int Priority => 10000; 23 | 24 | readonly List patterns; 25 | readonly Random random = new(); 26 | public static readonly string StatTalkedCount = "stat.talked-count"; 27 | 28 | public GreetingModule() 29 | { 30 | using var reader = new StreamReader(GetType().Assembly.GetManifestResourceStream("Citrine.Resources.greeting.json")); 31 | patterns = JsonConvert.DeserializeObject>(reader.ReadToEnd()); 32 | } 33 | 34 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 35 | { 36 | if (n.Text == null) 37 | return false; 38 | 39 | core.LikeWithLimited(n.User); 40 | 41 | var pattern = patterns.FirstOrDefault(record => Regex.IsMatch(n.Text.Trim().Replace("にゃ", "な"), record.Regex)); 42 | 43 | if (pattern == null) 44 | return false; 45 | 46 | await Task.Delay(random.Next(3000)); 47 | 48 | var message = (core.GetRatingOf(n.User)) switch 49 | { 50 | Rating.Hate => pattern.Hate(), 51 | Rating.Normal => pattern.Normal(), 52 | Rating.Like => pattern.Like(), 53 | Rating.BestFriend => pattern.BestFriend(), 54 | Rating.Partner => pattern.Partner(), 55 | _ => "", 56 | }; 57 | 58 | message = message 59 | .Replace("$user$", core.GetNicknameOf(n.User)) 60 | .Replace("$prefix$", FortuneModule.ItemPrefixes.Random()) 61 | .Replace("$item$", FortuneModule.Items.Random()); 62 | 63 | // 乱数 64 | message = Regex.Replace(message, @"\$rnd(\d+),(\d+)\$", (m) => 65 | { 66 | return random.Next(int.Parse(m.Groups[1].Value), int.Parse(m.Groups[2].Value)).ToString(); 67 | }); 68 | 69 | // からっぽは既読無視 70 | if (message != "") 71 | { 72 | EconomyModule.Pay(n, shell, core); 73 | core.Storage[n.User].Add(StatTalkedCount); 74 | await shell.ReplyAsync(n, message); 75 | } 76 | 77 | return true; 78 | } 79 | 80 | public class Pattern 81 | { 82 | [JsonProperty("regex")] 83 | public string Regex { get; set; } = ""; 84 | 85 | [JsonProperty("replyNormal")] 86 | public string[] ReplyNormal { get; set; } = new string[0]; 87 | 88 | [JsonProperty("replyPartner")] 89 | public string[] ReplyPartner { get; set; } = new string[0]; 90 | 91 | [JsonProperty("replyHate")] 92 | public string[] ReplyHate { get; set; } = new string[0]; 93 | 94 | [JsonProperty("replyBestFriend")] 95 | public string[] ReplyBestFriend { get; set; } = new string[0]; 96 | 97 | [JsonProperty("replyLike")] 98 | public string[] ReplyLike { get; set; } = new string[0]; 99 | } 100 | } 101 | 102 | public static class PatternExtension 103 | { 104 | public static string Hate(this GreetingModule.Pattern p) 105 | => p.ReplyHate?.Random() ?? p.Normal(); 106 | 107 | public static string Normal(this GreetingModule.Pattern p) 108 | => p.ReplyNormal?.Random() ?? "null"; 109 | 110 | public static string Like(this GreetingModule.Pattern p) 111 | => p.ReplyLike?.Random() ?? p.Normal(); 112 | 113 | public static string BestFriend(this GreetingModule.Pattern p) 114 | => p.ReplyBestFriend?.Random() ?? p.Like(); 115 | 116 | public static string Partner(this GreetingModule.Pattern p) 117 | => p.ReplyPartner?.Random() ?? p.BestFriend(); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (Misskey)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build-misskey", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/Citrine.Misskey/bin/Debug/netcoreapp3.0/Citrine.Misskey.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/Citrine.Misskey/bin/Debug/netcoreapp3.0/", 16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 17 | "console": "integratedTerminal", 18 | "stopAtEntry": false, 19 | "internalConsoleOptions": "openOnSessionStart" 20 | }, 21 | { 22 | "name": ".NET Core Launch (Standalone)", 23 | "type": "coreclr", 24 | "request": "launch", 25 | "preLaunchTask": "build-standalone", 26 | // If you have changed target frameworks, make sure to update the program path. 27 | "program": "${workspaceFolder}/Citrine.Standalone/bin/Debug/netcoreapp3.0/Citrine.Standalone.dll", 28 | "args": [], 29 | "cwd": "${workspaceFolder}/Citrine.Standalone/bin/Debug/netcoreapp3.0/", 30 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 31 | "console": "integratedTerminal", 32 | "stopAtEntry": false, 33 | "internalConsoleOptions": "openOnSessionStart" 34 | }, 35 | { 36 | "name": ".NET Core Launch (Mastodon)", 37 | "type": "coreclr", 38 | "request": "launch", 39 | "preLaunchTask": "build-mastodon", 40 | // If you have changed target frameworks, make sure to update the program path. 41 | "program": "${workspaceFolder}/Citrine.Mastodon/bin/Debug/netcoreapp3.0/Citrine.Mastodon.dll", 42 | "args": [], 43 | "cwd": "${workspaceFolder}/Citrine.Mastodon/bin/Debug/netcoreapp3.0/", 44 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 45 | "console": "integratedTerminal", 46 | "stopAtEntry": false, 47 | "internalConsoleOptions": "openOnSessionStart" 48 | }, 49 | { 50 | "name": ".NET Core Launch (Discord)", 51 | "type": "coreclr", 52 | "request": "launch", 53 | "preLaunchTask": "build-discord", 54 | // If you have changed target frameworks, make sure to update the program path. 55 | "program": "${workspaceFolder}/Citrine.Discord/bin/Debug/netcoreapp3.0/Citrine.Discord.dll", 56 | "args": [], 57 | "cwd": "${workspaceFolder}/Citrine.Discord/bin/Debug/netcoreapp3.0/", 58 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 59 | "console": "integratedTerminal", 60 | "stopAtEntry": false, 61 | "internalConsoleOptions": "openOnSessionStart" 62 | }, 63 | { 64 | "name": ".NET Core Launch (Sea)", 65 | "type": "coreclr", 66 | "request": "launch", 67 | "preLaunchTask": "build-sea", 68 | // If you have changed target frameworks, make sure to update the program path. 69 | "program": "${workspaceFolder}/Citrine.Sea/bin/Debug/netcoreapp3.0/Citrine.Sea.dll", 70 | "args": [], 71 | "cwd": "${workspaceFolder}/Citrine.Sea/bin/Debug/netcoreapp3.0/", 72 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window 73 | "console": "integratedTerminal", 74 | "stopAtEntry": false, 75 | "internalConsoleOptions": "openOnSessionStart" 76 | }, 77 | { 78 | "name": ".NET Core Attach", 79 | "type": "coreclr", 80 | "request": "attach", 81 | "processId": "${command:pickProcess}" 82 | }, 83 | ] 84 | } -------------------------------------------------------------------------------- /Modules/KawaiiModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using System.Threading.Tasks; 6 | using BotBone.Core; 7 | using BotBone.Core.Api; 8 | using BotBone.Core.Modules; 9 | 10 | namespace Citrine.Core.Modules 11 | { 12 | public partial class KawaiiModule : ModuleBase 13 | { 14 | public override int Priority => -9999; 15 | 16 | public static readonly string StatKawaiiTeachedCount = "stat.kawaii.teached-count"; 17 | public static readonly string StatKawaiiQuestionedCount = "stat.kawaii.questioned-count"; 18 | public static readonly string StatKawaiiQueriedCount = "stat.kawaii.queried-count"; 19 | 20 | public async override Task ActivateAsync(IPost n, IShell shell, Server core) 21 | { 22 | if (n.Text != null) 23 | { 24 | var storage = core.GetMyStorage(); 25 | var map = storage.Get("kawaiiMap", new Dictionary()); 26 | 27 | void SaveMap() => storage.Set("kawaiiMap", map); 28 | 29 | if (storage.Has("kawaiiList")) 30 | { 31 | // レガシーな可愛いリストがあったらマイグレをする 32 | var list = storage.Get("kawaiiList", new List()); 33 | list.ForEach(k => map[k] = "かわいい"); 34 | logger.Info($"Migrated {list.Count} kawaii things from the list."); 35 | storage.Clear("kawaiiList"); 36 | } 37 | 38 | var input = n.Text.TrimMentions(); 39 | 40 | var adjectiveCombined = $"{adjectiveKawaii}|{adjectiveKawaikunai}|{adjectiveOishii}|{adjectiveMazui}"; 41 | 42 | var teacherPattern = Regex.Match(input, $"(.+)(?:は|って)({adjectiveCombined})[でっ]?す?の?よ?"); 43 | var questionPattern = Regex.Match(input, $"(.+)(?:は|って)({adjectiveCombined})(?:と(?:(?:思|おも)う)|ですか|の|かな)?[??]"); 44 | var queryPattern = Regex.Match(input, $"({adjectiveCombined})(?:もの|物|の|人|子|娘|ひと)(?:(?:は|って|とは|)(?:何|なに|誰|どなた|何方)|を(?:教|おし)えて)?[??]*"); 45 | var isKawaii = input.IsMatch(adjectiveKawaii); 46 | 47 | if (questionPattern.Success) 48 | { 49 | await Task.Delay(4000); 50 | var target = questionPattern.Groups[1].Value.Trim().ToLowerInvariant(); 51 | 52 | var response = map.ContainsKey(target) ? map[target] : "わかりません"; 53 | core.Storage[n.User].Add(StatKawaiiQuestionedCount); 54 | await shell.ReplyAsync(n, response); 55 | } 56 | else if (teacherPattern.Success) 57 | { 58 | await Task.Delay(4000); 59 | var targetUnnormalized = teacherPattern.Groups[1].Value.Trim(); 60 | var target = targetUnnormalized.ToLowerInvariant(); 61 | var adj = teacherPattern.Groups[2].Value.Trim(); 62 | 63 | if (target.IsMatch("シトリン|しとりん|citrine") && target.IsMatch(adjectiveKawaii)) 64 | { 65 | // 自分を褒めてくれる場合は後続のモジュールが処理してくれることを期待 66 | return false; 67 | } 68 | 69 | // 記憶 70 | map[target] = adj; 71 | SaveMap(); 72 | core.Storage[n.User].Add(StatKawaiiTeachedCount); 73 | await shell.ReplyAsync(n, $"{targetUnnormalized}は{adj}... 覚えました"); 74 | } 75 | else if (queryPattern.Success) 76 | { 77 | await Task.Delay(4000); 78 | // ランダムに可愛いものを引く 79 | var adj = queryPattern.Groups[1].Value.Trim(); 80 | var regex = 81 | adj.IsMatch(adjectiveKawaii) ? adjectiveKawaii : 82 | adj.IsMatch(adjectiveKawaikunai) ? adjectiveKawaikunai : 83 | adj.IsMatch(adjectiveOishii) ? adjectiveOishii : 84 | adj.IsMatch(adjectiveMazui) ? adjectiveMazui : null; 85 | 86 | var suggest = regex != null ? map.Where(kv => kv.Value.IsMatch(regex)).Select(kv => kv.Key).Random() : null; 87 | core.Storage[n.User].Add(StatKawaiiQueriedCount); 88 | await shell.ReplyAsync(n, suggest != null ? $"{suggest}はどう? {adj}よ" : $"{adj}もの,知らないです"); 89 | } 90 | else 91 | { 92 | return false; 93 | } 94 | EconomyModule.Pay(n, shell, core); 95 | core.LikeWithLimited(n.User); 96 | return true; 97 | } 98 | 99 | return false; 100 | } 101 | 102 | private const string adjectiveKawaii = "(?:か[あわ]い|可愛|カワイ)[いイー〜]?"; 103 | private const string adjectiveKawaikunai = "(?:可愛|かわい|カワイ)くない"; 104 | private const string adjectiveOishii = "(?:美味|おい|うま|旨)し?[いー〜]?"; 105 | private const string adjectiveMazui = "(?:美味|おい)しくない|(?:まず|不味)[いイ]"; 106 | 107 | private readonly Logger logger = new Logger(nameof(KawaiiModule)); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Modules/FortuneModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Threading.Tasks; 7 | using BotBone.Core; 8 | using BotBone.Core.Api; 9 | using BotBone.Core.Modules; 10 | 11 | namespace Citrine.Core.Modules 12 | { 13 | using static FortuneModule; 14 | using static FortuneExtension; 15 | 16 | public partial class FortuneModule : ModuleBase 17 | { 18 | public static readonly string StatFortuneCount = "stat.fortune-count"; 19 | 20 | public async override Task ActivateAsync(IPost n, IShell shell, Server core) 21 | { 22 | if (n.Text != null && Regex.IsMatch(n.Text.ToLowerInvariant(), "占|運勢|みくじ|fortune")) 23 | { 24 | core.LikeWithLimited(n.User); 25 | var isPremium = EconomyModule.HasItem(n.User, "fortuneplus", core); 26 | var r = new Random(n.User.Id.GetHashCode() + DateTime.Now.Day + DateTime.Now.Month - DateTime.Now.Year); 27 | 28 | int min = 1; 29 | int max = 6; 30 | 31 | int love = r.Next(min, max), 32 | money = r.Next(min, max), 33 | work = r.Next(min, max), 34 | study = r.Next(min, max); 35 | 36 | // premium 37 | int health = r.Next(min, max), 38 | hobby = r.Next(min, max), 39 | sns = r.Next(min, max), 40 | gaming = r.Next(min, max), 41 | meal = r.Next(min, max), 42 | goingOut = r.Next(min, max), 43 | shopping = r.Next(min, max); 44 | 45 | var builder = new StringBuilder(); 46 | 47 | var list = new List<(string name, string emoji, int value, string bestMessage, string worstMessage)> 48 | { 49 | ("恋愛運", "❤", love, "気になるあの人にアタックしてみては...?", "グイグイ迫るとかえって痛い目を見るかも."), 50 | ("金運", "💰", money, "意外なことで得するかも.", "ぼったくりには気をつけてね."), 51 | ("仕事運", "💻", work, "日頃の頑張りがきっと報われるよ.", "やる気が空回りして,大ミスしちゃわないように気をつけてね."), 52 | ("勉強運", "📒", study, "昨日わからなかったことがわかる日かも.", "無理して勉強しても頭に入らないかも.") 53 | }; 54 | 55 | if (isPremium) 56 | { 57 | list.Add(("健康運", "💪", health, "今日一日バリバリ過ごせるでしょう.", "風邪を引かないよう気をつけて.")); 58 | list.Add(("趣味運", "🎸", hobby, "好きなことに打ち込もう!", "挫折に気をつけてね.")); 59 | list.Add(("食事運", "🍣", meal, "奮発して美味しい出前を取ろう.", "まずい食べ物に巡り合っちゃうかも.")); 60 | list.Add(("SNS運", "💬", sns, "喧嘩なく, 平和に過ごせるよ.", "何気ない発言で炎上しちゃうかも.気をつけて.")); 61 | list.Add(("ゲーム運", "🎮", gaming, "スコアが伸びる日だよ.やり込もう!", "頑張ってもスコアが伸びない日だよ.無理はしないで.")); 62 | list.Add(("おでかけ運", "🏠", goingOut, "気分転換にお散歩に行くと良いかも.", "今日は転びやすいかも.気をつけてね.")); 63 | list.Add(("買いもの運", "👜", shopping, "思い切って,欲しかったものを買ってみては?", "不良品を引いちゃうかも.お買い物は程々に.")); 64 | } 65 | 66 | var avg = (int)Math.Round(list.Average(el => el.value)); 67 | 68 | builder.AppendLine($"***{results[avg - 1]}***"); 69 | 70 | list.ForEach(r => builder.AppendLine($"{r.name}{r.emoji}: {GetStar(r.value, 5)}")); 71 | 72 | var luckyItem = GenerateWord(r); 73 | 74 | builder.AppendLine($"ラッキーアイテム💎: {luckyItem}"); 75 | 76 | if (isPremium) 77 | { 78 | var orderby = list.OrderByDescending(r => r.value); 79 | var (name, emoji, value, bestMessage, worstMessage) = orderby.First(); 80 | var worst = orderby.Last(); 81 | builder 82 | .AppendLine() 83 | .Append("シトリンから一言: ") 84 | .Append($"{worst.name}が低いね.{worst.worstMessage}") 85 | .AppendLine($"{name}が高いね.{bestMessage}"); 86 | } 87 | 88 | core.Storage[n.User].Add(StatFortuneCount); 89 | await shell.ReplyAsync(n, builder.ToString(), $"今日の{core.GetNicknameOf(n.User)}の運勢"); 90 | EconomyModule.Pay(n, shell, core); 91 | return true; 92 | } 93 | 94 | return false; 95 | } 96 | 97 | public override Task OnDmReceivedAsync(IPost n, IShell shell, Server core) => ActivateAsync(n, shell, core); 98 | 99 | static string GetStar(int value, int maxValue) => new string('★', value) + new string('☆', maxValue - value); 100 | 101 | } 102 | 103 | static class FortuneExtension 104 | { 105 | public static string GenerateWord(Random? r = null) 106 | { 107 | var sb = new StringBuilder(); 108 | var p = ItemPrefix(r); 109 | var i = Item(r); 110 | var s = ItemSuffix(r); 111 | if ((r ?? rnd).Next(100) > 50) 112 | sb.Append(p); 113 | sb.Append(i); 114 | if ((r ?? rnd).Next(100) > 70) 115 | sb.Append(s); 116 | return sb.ToString(); 117 | } 118 | 119 | public static string Item(Random? r = null) => Items.Random(r); 120 | public static string ItemPrefix(Random? r = null) => ItemPrefixes.Random(r); 121 | public static string ItemSuffix(Random? r = null) => ItemSuffixes.Random(r); 122 | 123 | static readonly Random rnd = new Random(); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /Modules/BirthdayModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using System.Threading.Tasks; 6 | using System.Timers; 7 | using BotBone.Core; 8 | using BotBone.Core.Api; 9 | using BotBone.Core.Modules; 10 | 11 | namespace Citrine.Core.Modules 12 | { 13 | public class BirthdayModule : ModuleBase 14 | { 15 | public BirthdayModule() 16 | { 17 | timer = new Timer(1000) 18 | { 19 | AutoReset = true, 20 | Enabled = true, 21 | }; 22 | timer.Elapsed += OnTick; 23 | } 24 | 25 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 26 | { 27 | if (n.Text == null) 28 | return false; 29 | 30 | var storage = core.Storage[n.User]; 31 | 32 | var m = patternQueryBirthday.Match(n.Text); 33 | 34 | if (m.Success) 35 | { 36 | var birthday = storage.Get(StorageKey.Birthday, DateTime.MinValue); 37 | var output = birthday == DateTime.MinValue ? "知らないよ〜?" : $"{birthday:yyyy年MM月dd日}だよね"; 38 | await shell.ReplyAsync(n, $"{core.GetNicknameOf(n.User)}の誕生日は" + output); 39 | return true; 40 | } 41 | 42 | m = patternSetBirthday.Match(n.Text); 43 | if (m.Success) 44 | { 45 | // 嫌いな人は相手にしない 46 | if (core.GetRatingOf(n.User) <= Rating.Hate) 47 | return false; 48 | 49 | await SetBirthday(n, shell, core, m.Groups[1].Value); 50 | return true; 51 | } 52 | 53 | m = patternStartBirthdayRegister.Match(n.Text); 54 | if (m.Success) 55 | { 56 | // 嫌いな人は相手にしない 57 | if (core.GetRatingOf(n.User) <= Rating.Hate) 58 | return false; 59 | 60 | var res = await shell.ReplyAsync(n, "いいよ〜. 誕生日の日付を教えてね(2020/4/10 みたいな形式でお願い)"); 61 | if (res != null) 62 | core.RegisterContext(res, this, null); 63 | return true; 64 | } 65 | 66 | return false; 67 | } 68 | 69 | public override async Task OnTimelineAsync(IPost n, IShell shell, Server core) 70 | { 71 | (this.core, this.shell) = (core, shell); 72 | await Task.Delay(0); 73 | return false; 74 | } 75 | 76 | public override async Task OnRepliedContextually(IPost n, IPost? context, Dictionary store, IShell shell, Server core) 77 | { 78 | if (n.Text == null) 79 | return false; 80 | 81 | var m = patternBirthday.Match(n.Text); 82 | 83 | if (!m.Success) 84 | { 85 | await shell.ReplyAsync(n, "ごめん,正しい日付でお願い"); 86 | return true; 87 | } 88 | 89 | await SetBirthday(n, shell, core, m.Groups[1].Value); 90 | return true; 91 | } 92 | private async void OnTick(object sender, ElapsedEventArgs e) 93 | { 94 | if (core == null || shell == null) 95 | return; 96 | 97 | // 祝う対象を抽出する 98 | var birthDayPeople = core.Storage.Records.Where(kv => 99 | { 100 | var (userId, storage) = kv; 101 | 102 | // 好感度が Like 以上 103 | var isLike = core.GetRatingOf(userId) >= Rating.Like; 104 | 105 | // 本日が誕生日である 106 | var birthday = storage.Get(StorageKey.Birthday, DateTime.MinValue); 107 | if (birthday == DateTime.MinValue) return false; 108 | var today = DateTime.Today; 109 | var birthdayIsToday = birthday.Month == today.Month && birthday.Day == today.Day; 110 | 111 | // まだ祝ってない 112 | var isNotCelebratedYet = storage.Get(keyLastCelebratedYear, 0) != today.Year; 113 | 114 | return isLike && birthdayIsToday && isNotCelebratedYet; 115 | }); 116 | 117 | foreach (var (id, storage) in birthDayPeople) 118 | { 119 | var user = await shell.GetUserAsync(id); 120 | if (user == null) continue; 121 | 122 | await shell.SendDirectMessageAsync(user, $"誕生日おめでとう,{core.GetNicknameOf(user)}"); 123 | storage.Set(keyLastCelebratedYear, DateTime.Today.Year); 124 | } 125 | 126 | } 127 | 128 | private async Task SetBirthday(IPost n, IShell shell, Server core, string value) 129 | { 130 | var storage = core.Storage[n.User]; 131 | try 132 | { 133 | var birthday = DateTime.Parse(value); 134 | storage.Set(StorageKey.Birthday, birthday); 135 | await shell.ReplyAsync(n, "覚えたよ"); 136 | } 137 | catch (FormatException) 138 | { 139 | await shell.ReplyAsync(n, "ごめん, 正しい日付じゃないよそれ..."); 140 | } 141 | } 142 | 143 | private const string date = @"(\d{1,4}[年/\-]\d{1,2}[月/\-]\d{1,2}[日/\-]?)"; 144 | private static readonly Regex patternBirthday = new Regex(date); 145 | private static readonly Regex patternSetBirthday = new Regex($"誕生日は{date}"); 146 | private static readonly Regex patternStartBirthdayRegister = new Regex("誕生日を?(覚|おぼ)"); 147 | private static readonly Regex patternQueryBirthday = new Regex("誕生日(わか|分か|知って)"); 148 | private static readonly string keyLastCelebratedYear = "birthday.last-celebrated"; 149 | 150 | private readonly Timer timer; 151 | private Server? core; 152 | private IShell? shell; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /Modules/EconomyModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | using System.Threading.Tasks; 6 | using BotBone.Core; 7 | using BotBone.Core.Api; 8 | using BotBone.Core.Modules; 9 | 10 | namespace Citrine.Core.Modules 11 | { 12 | public class EconomyModule : ModuleBase 13 | { 14 | public override int Priority => -9000; 15 | 16 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 17 | { 18 | if (n.Text is string text) 19 | { 20 | if (text.Contains("所持金")) 21 | { 22 | var balance = core.Storage[n.User].Get("economy.balance", 0); 23 | await shell.ReplyAsync(n, $"{core.GetNicknameOf(n.User)}の所持金は,{balance} クォーツです"); 24 | return true; 25 | } 26 | if (text.IsMatch("[買か]い(物|もの)|ク[オォ]ーツショップ")) 27 | { 28 | var builder = new StringBuilder(); 29 | builder.AppendLine("ようこそクォーツショップへ.お取り扱いしている商品はこちらです"); 30 | builder.AppendLine(string.Join("\n\n", ShopItems.Select(i => $"{i.DisplayName} {i.Price}クォーツ\n {i.Description}"))); 31 | builder.Append("欲しい商品があったら,「〜〜をください」って話しかけてくださいね"); 32 | await shell.ReplyAsync(n, builder.ToString()); 33 | return true; 34 | } 35 | var mention = StringExtension.RegexMentions.ToString(); 36 | var m1 = Regex.Match(text, $@"({mention})[にへ](\d+)ク[オォ]ーツを?送金"); 37 | var m2 = Regex.Match(text, $@"(\d+)ク[オォ]ーツを? *({mention}) *[にへ]送金"); 38 | if (m1.Success) 39 | await TransferQuartzAsync(m1.Groups[1].Value, m1.Groups[2].Value, n, shell, core); 40 | else if (m2.Success) 41 | await TransferQuartzAsync(m2.Groups[2].Value, m2.Groups[1].Value, n, shell, core); 42 | foreach (var item in ShopItems) 43 | { 44 | if (text.TrimMentions().IsMatch($"^{Regex.Escape(item.DisplayName)}を(ください|ちょうだい|くれ|頂戴)")) 45 | { 46 | string res; 47 | if (HasItem(n.User, item.Id, core)) 48 | { 49 | res = item.DisplayName + " は既に持ってるみたいですよ"; 50 | } 51 | else if (TryUseMoney(n.User, item.Price, core)) 52 | { 53 | GiveItem(n.User, item.Id, core); 54 | res = $"お買い上げありがとうございます. ({item.DisplayName} を手に入れました)"; 55 | } 56 | else 57 | { 58 | res = "お金が足りません."; 59 | } 60 | await shell.ReplyAsync(n, res); 61 | return true; 62 | } 63 | } 64 | } 65 | return false; 66 | } 67 | 68 | public static void Pay(IPost n, IShell _, Server core) 69 | { 70 | // 好感度に応じて、ランダムな量のお金をあげる 71 | var storage = core.Storage[n.User]; 72 | int addition = rnd.Next(1, 10) * 73 | core.GetRatingOf(n.User) switch 74 | { 75 | Rating.Partner => 10, 76 | Rating.BestFriend => 5, 77 | Rating.Like => 2, 78 | Rating.Hate => 0, 79 | _ => 1, 80 | }; 81 | storage.Add("economy.balance", addition); 82 | } 83 | 84 | public static bool TryUseMoney(IUser user, int amount, Server core) 85 | { 86 | var storage = core.Storage[user]; 87 | var balance = storage.Get("economy.balance", 0); 88 | if (balance < amount) 89 | return false; 90 | storage.Set("economy.balance", balance - amount); 91 | return true; 92 | } 93 | 94 | public static bool HasItem(IUser user, string itemId, Server core) => core.Storage[user].Get("economy.items." + itemId, false); 95 | 96 | public static void GiveItem(IUser user, string itemId, Server core) => core.Storage[user].Set("economy.items." + itemId, true); 97 | 98 | public static void TakeItem(IUser user, string itemId, Server core) => core.Storage[user].Set("economy.items." + itemId, false); 99 | 100 | private async Task TransferQuartzAsync(string targetMention, string value2, IPost n, IShell shell, Server core) 101 | { 102 | var target = await shell.GetUserByNameAsync(targetMention); 103 | if (target == null) 104 | { 105 | await shell.ReplyAsync(n, "ユーザーが見つからなかったため, 送金に失敗しました."); 106 | return; 107 | } 108 | var amount = int.Parse(value2); 109 | if (!TryUseMoney(n.User, amount, core)) 110 | { 111 | await shell.ReplyAsync(n, "所持金が送金分よりも少ないため, 送金に失敗しました. あなたの残高は, " + core.Storage[n.User].Get("economy.balance", 0) + "クォーツです."); 112 | return; 113 | } 114 | core.Storage[target].Add("economy.balance", amount); 115 | await shell.ReplyAsync(n, "送金しました. あなたの残高は, " + core.Storage[n.User].Get("economy.balance", 0) + "クォーツです."); 116 | } 117 | 118 | private static readonly Random rnd = new Random(); 119 | 120 | private readonly ShopItem[] ShopItems = 121 | { 122 | new ShopItem("fortuneplus", "占い+", "占い機能を増強します。", 1000), 123 | }; 124 | } 125 | 126 | public struct ShopItem 127 | { 128 | public string DisplayName { get; set; } 129 | public string Id { get; set; } 130 | public string Description { get; set; } 131 | public int Price { get; set; } 132 | 133 | public ShopItem(string id, string displayName, string description, int price) => (Id, DisplayName, Description, Price) = (id, displayName, description, price); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Modules/TranslateModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Text.RegularExpressions; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | using BotBone.Core; 8 | using BotBone.Core.Api; 9 | using BotBone.Core.Modules; 10 | 11 | namespace Citrine.Core.Modules 12 | { 13 | public class TranslateModule : ModuleBase 14 | { 15 | public override int Priority => -1000; 16 | 17 | public static readonly string StatTranslatedCount = "stat.translated-count"; 18 | public static readonly string StatRetranslatedCount = "stat.retranslated-count"; 19 | 20 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 21 | { 22 | if (n.User.Id == shell.Myself?.Id) 23 | return false; 24 | 25 | if (n.Text == null) 26 | return false; 27 | 28 | foreach (var (pattern, code) in langs) 29 | { 30 | var matchNormal = Regex.Match(n.Text.TrimMentions(), $"(.+)を{pattern}[にへ]?翻?訳"); 31 | var matchReposted = Regex.Match(n.Text.TrimMentions(), $"これを?{pattern}[にへ]?翻?訳"); 32 | if (matchReposted.Success && n.Repost != null) 33 | { 34 | if (string.IsNullOrEmpty(n.Repost.Text)) 35 | { 36 | await shell.ReplyAsync(n, "ん,その投稿にはテキストが含まれていないですね.無いものは翻訳できないです"); 37 | return true; 38 | } 39 | await TranslateAsync("auto", code, n.Repost.Text, n, shell, core); 40 | core.Storage[n.User].Add(StatTranslatedCount); 41 | return true; 42 | } 43 | else if (matchNormal.Success) 44 | { 45 | await TranslateAsync("auto", code, HttpUtility.UrlEncode(matchNormal.Groups[1].Value), n, shell, core); 46 | core.Storage[n.User].Add(StatTranslatedCount); 47 | return true; 48 | } 49 | } 50 | 51 | return false; 52 | } 53 | 54 | public override async Task OnRepliedContextually(IPost n, IPost? context, Dictionary store, IShell shell, Server core) 55 | { 56 | if (n.User.Id == shell.Myself?.Id) 57 | return false; 58 | 59 | if (n.Text == null) 60 | return false; 61 | 62 | foreach (var (pattern, code) in langs) 63 | { 64 | var m = Regex.Match(n.Text.TrimMentions(), $"{pattern}[にへ]再翻訳"); 65 | if (m.Success) 66 | { 67 | await TranslateAsync((string)store["code"], code, (string)store["result"], n, shell, core); 68 | core.Storage[n.User].Add(StatRetranslatedCount); 69 | return true; 70 | } 71 | } 72 | 73 | return false; 74 | } 75 | 76 | private async Task TranslateAsync(string from, string to, string text, IPost n, IShell shell, Server core) 77 | { 78 | core.LikeWithLimited(n.User); 79 | var result = await core.ExecCommand($"/translate {from} {to} {text}"); 80 | var reply = await shell.ReplyAsync(n, result); 81 | 82 | if (reply == null) 83 | return; 84 | 85 | EconomyModule.Pay(n, shell, core); 86 | core.LikeWithLimited(n.User); 87 | core.RegisterContext(reply, this, new Dictionary() 88 | { 89 | { "result", result}, 90 | { "code", to }, 91 | }); 92 | } 93 | 94 | private readonly (string pattern, string code)[] langs = { 95 | ("アイスランド語", "is"), 96 | ("アイルランド語", "ga"), 97 | ("アゼルバイジャン語", "az"), 98 | ("アフリカーンス語", "af"), 99 | ("アムハラ語", "am"), 100 | ("アラビア語", "ar"), 101 | ("アルバニア語", "sq"), 102 | ("アルメニア語", "hy"), 103 | ("イタリア語", "it"), 104 | ("イディッシュ語", "yi"), 105 | ("イボ語", "ig"), 106 | ("インドネシア語", "id"), 107 | ("ウェールズ語", "cy"), 108 | ("ウクライナ語", "uk"), 109 | ("ウズベク語", "uz"), 110 | ("ウルドゥー?語", "ur"), 111 | ("エストニア語", "et"), 112 | ("エスペラント語", "eo"), 113 | ("オランダ語", "dv"), 114 | ("カザフ語", "kk"), 115 | ("カタルーニャ語", "ca"), 116 | ("ガリシア語", "gl"), 117 | ("ギリシ[アャ]語", "el"), 118 | ("キルギス語", "ky"), 119 | ("グジャラー?ト語", "gu"), 120 | ("(カンボジア|クメール)語", "km"), 121 | ("クルド語", "ku"), 122 | ("クロアチア語", "hr"), 123 | ("コー?サ語", "xh"), 124 | ("コルシカ語", "co"), 125 | ("サモア語", "sm"), 126 | ("ジャワ語", "jv"), 127 | ("(グル|ジョー)ジア語", "ka"), 128 | ("ショナ語", "sn"), 129 | ("シンド語", "sd"), 130 | ("シンハラ語", "si"), 131 | ("スウェーデン語", "sv"), 132 | ("ズールー語", "zu"), 133 | ("スコットランド ?ゲール語", "gd"), 134 | ("スペイン語", "es"), 135 | ("スロバキア語", "sk"), 136 | ("スロベニア語", "sl"), 137 | ("スワヒリ語", "sw"), 138 | ("スンダ語", "su"), 139 | ("セブアノ語", "ceb"), 140 | ("セルビア語", "sr"), 141 | ("ソト語", "st"), 142 | ("ソマリ語", "so"), 143 | ("タイ語", "th"), 144 | ("タガログ語", "tl"), 145 | ("タジク語", "tg"), 146 | ("タミル語", "ta"), 147 | ("チェコ語", "cs"), 148 | ("(チェワ|ニャンジャ)語", "ny"), 149 | ("テルグ語", "te"), 150 | ("デンマーク語", "da"), 151 | ("ドイツ語", "de"), 152 | ("トルコ語", "tr"), 153 | ("ネパール語", "ne"), 154 | ("ノルウェー語", "no"), 155 | ("ハイチ語", "ht"), 156 | ("ハウサ語", "ha"), 157 | ("パシュトー?語", "ps"), 158 | ("バスク語", "eu"), 159 | ("ハワイ語", "haw"), 160 | ("(ハンガリー|マジャル)語", "hu"), 161 | ("パンジャー?ブ語", "pa"), 162 | ("ヒンディー語", "hi"), 163 | ("フィンランド語", "fi"), 164 | ("フランス語", "fr"), 165 | ("フリジア語", "fy"), 166 | ("ブルガリア語", "bg"), 167 | ("ベトナム語", "vi"), 168 | ("ヘブライ語", "he"), 169 | ("ベラルーシ語", "be"), 170 | ("ペルシ[アャ]語", "fa"), 171 | ("ベンガル語", "bn"), 172 | ("ポーランド語", "pl"), 173 | ("ボスニア語", "bs"), 174 | ("ポルトガル語", "pt"), 175 | ("マオリ語", "mi"), 176 | ("マケドニア語", "mk"), 177 | ("マラーティー語", "mr"), 178 | ("マラガシ語", "mg"), 179 | ("マラヤーラム語", "ml"), 180 | ("マルタ語", "mt"), 181 | ("マレー語", "ms"), 182 | ("ミャンマー語", "my"), 183 | ("モンゴル語", "mn"), 184 | ("モン語", "hmn"), 185 | ("ヨルバ語", "yo"), 186 | ("ラオ語", "lo"), 187 | ("ラテン語", "la"), 188 | ("ラトビア語", "lv"), 189 | ("リトアニア語", "lt"), 190 | ("ルーマニア語", "ro"), 191 | ("ルクセンブルク語", "lb"), 192 | ("ロシア語", "ru"), 193 | ("英語", "en"), 194 | ("(韓国|朝鮮)語", "ko"), 195 | ("中国語", "zh"), 196 | ("(中国語)(簡体)", "zh-CN"), 197 | ("(中国語)(繁体)", "zh-TW"), 198 | ("日本語", "ja"), 199 | }; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /Modules/SearchModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | using System.Threading.Tasks; 5 | using System.Web; 6 | using BotBone.Core; 7 | using BotBone.Core.Api; 8 | using BotBone.Core.Modules; 9 | using Newtonsoft.Json; 10 | using Newtonsoft.Json.Linq; 11 | 12 | namespace Citrine.Core.Modules 13 | { 14 | public class SearchModule : ModuleBase 15 | { 16 | public override int Priority => -100; 17 | 18 | public static readonly string StatSearchedCount = "stat.searched-count"; 19 | public static readonly string StatCalculatedCount = "stat.calculated-count"; 20 | 21 | private static readonly string CalcApiUrl = "http://www.rurihabachi.com/web/webapi/calculator/json?exp={0}"; 22 | private static readonly string WikipediaApiUrl = "https://ja.wikipedia.org/w/api.php?action=query&format=json&prop=extracts&redirects=1&exchars=300&explaintext=1&titles={0}"; 23 | private static readonly string EnWikipediaApiUrl = "https://en.wikipedia.org/w/api.php?action=query&format=json&prop=extracts&redirects=1&exchars=300&explaintext=1&titles={0}"; 24 | private static readonly string NicopediaApiUrl = "http://api.nicodic.jp/page.summary/n/a/{0}"; 25 | private static readonly Regex regexMath = new Regex(@"^([1234567890\+\-\*/\(\)\^piesqrtlogabnc]+?)(っ?て|と?は|[==])"); 26 | private static readonly Regex regexPedia = new Regex(@"(.+?)((っ?て|と?は)(何|なに|なん|誰|だれ|どなた|何方)|について(教|おし)えて)"); 27 | 28 | private static readonly (string regex, string value)[] myDictionary = { 29 | ("シトリン|citrine|しとりん", "私の名前です."), 30 | ("ゼルチカ|ぜるちか|xeltica|ぜるち", "私の生みの親です."), 31 | ("生命、?宇宙、?そして万物についての究極の疑問の(答|こた)え|answer to the ultimate question of life,? the universe,? and everything|人類、?宇宙、?(全|すべ)ての(答|こた)え", "42"), 32 | }; 33 | 34 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 35 | { 36 | if (n.User.Id == shell.Myself?.Id) 37 | return false; 38 | if (n.Text == default) 39 | return false; 40 | 41 | var m = regexMath.Match(n.Text.TrimMentions()); 42 | var mpedia = regexPedia.Match(n.Text); 43 | if (m.Success || mpedia.Success) 44 | { 45 | string? response = default; 46 | string? query = default; 47 | if (m.Success) 48 | { 49 | query = m.Groups[1].Value.TrimMentions(); 50 | response = await FromCalcAsync(query); 51 | core.Storage[n.User].Add(StatCalculatedCount); 52 | } 53 | else if (mpedia.Success) 54 | { 55 | query = mpedia.Groups[1].Value.TrimMentions(); 56 | response = FromMyKnowledge(query); 57 | response ??= await FromWikipediaAsync(query, WikipediaApiUrl, "ja"); 58 | response ??= await FromWikipediaAsync(query, EnWikipediaApiUrl, "en"); 59 | response ??= await FromNicopediaAsync(query); 60 | core.Storage[n.User].Add(StatSearchedCount); 61 | } 62 | 63 | response ??= $"すみません,調べてみたけどわかりませんでした."; 64 | await shell.ReplyAsync(n, response); 65 | 66 | EconomyModule.Pay(n, shell, core); 67 | core.LikeWithLimited(n.User); 68 | return true; 69 | } 70 | return false; 71 | } 72 | 73 | private string FromMyKnowledge(string query) 74 | => myDictionary.FirstOrDefault(dic => Regex.IsMatch(query, dic.regex)).value; 75 | 76 | private async Task FromNicopediaAsync(string query) 77 | { 78 | var json = await (await Server.Http.GetAsync(CreateUrl(NicopediaApiUrl, query))).Content.ReadAsStringAsync(); 79 | // JSONP なので対策 80 | json = json.Remove(json.Length - 2, 2).Remove(0, 2); 81 | if (json.Trim() == "null") 82 | return null; 83 | var res = JObject.Parse(json); 84 | var summary = res["summary"].ToObject(); 85 | var title = res["title"].ToObject().Replace(" ", "_"); 86 | return $@"「{title}」について調べてきました. 87 | > {summary}... 88 | 89 | 出典: https://dic.nicovideo.jp/a/{HttpUtility.UrlEncode(title)} 90 | "; 91 | 92 | } 93 | 94 | private async Task FromCalcAsync(string query) 95 | { 96 | var res = await (await Server.Http.GetAsync(CreateUrl(CalcApiUrl, query))).Content.ReadAsStringAsync(); 97 | var json = JsonConvert.DeserializeObject(res); 98 | if (json.Status > 0) 99 | return json.Status == 60 ? $"{query} は計算できないよ" : default; 100 | return $"{json.Expression} = {json.Value[0].CalculatedValue}"; 101 | } 102 | 103 | public async Task FromWikipediaAsync(string query, string url, string langCode) 104 | { 105 | var res = JObject.Parse(await (await Server.Http.GetAsync(CreateUrl(url, query))).Content.ReadAsStringAsync()); 106 | if (!res.ContainsKey("query")) 107 | { 108 | return default; 109 | } 110 | var q = res["query"] as JObject; 111 | if (!q?.ContainsKey("pages") ?? false) 112 | { 113 | return default; 114 | } 115 | if (q == null) return default; 116 | var pages = q["pages"].First.First; 117 | if (pages["extract"] == null) 118 | return default; 119 | var text = pages["extract"].ToObject(); 120 | var title = pages["title"].ToObject(); 121 | 122 | // 整形 123 | // headingを表す == が出てきた当たりで切ればいい感じになる気がする。ダメでも240文字までにトリミングする。 124 | if (text.IndexOf("==") > 0) 125 | text = text.Substring(0, text.IndexOf("==")); 126 | if (text.Length > 240) 127 | text = text.Substring(0, 240); 128 | text = text.Replace("\n", "").Replace("\r", ""); 129 | 130 | return $@"「{title}」について調べてきました. 131 | > {text} 132 | 133 | 出典: https://{langCode}.wikipedia.org/wiki/{HttpUtility.UrlEncode(title.Replace(" ", "_"))}"; 134 | } 135 | 136 | public static string CreateUrl(string path, params string[] pars) => string.Format(path, pars.Select(HttpUtility.UrlEncode).ToArray()); 137 | 138 | public class CalcModel 139 | { 140 | [JsonProperty("expression")] 141 | public string Expression { get; set; } = ""; 142 | [JsonProperty("status")] 143 | public int Status { get; set; } 144 | [JsonProperty("message")] 145 | public string Message { get; set; } = ""; 146 | [JsonProperty("count")] 147 | public int Count { get; set; } 148 | [JsonProperty("value")] 149 | public CalcModelValue[] Value { get; set; } = new CalcModelValue[0]; 150 | 151 | public class CalcModelValue 152 | { 153 | [JsonProperty("calculatedvalue")] 154 | public string CalculatedValue { get; set; } = ""; 155 | } 156 | } 157 | 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Modules/ApexLegendsModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Text.RegularExpressions; 5 | using System.Threading.Tasks; 6 | using BotBone.Core; 7 | using BotBone.Core.Api; 8 | using BotBone.Core.Modules; 9 | using Newtonsoft.Json; 10 | 11 | namespace Citrine.Core.Modules 12 | { 13 | public class ApexLegendsMapRotationModule : ModuleBase 14 | { 15 | public override async Task ActivateAsync(IPost n, IShell shell, Server core) 16 | { 17 | if (n.Text == null) 18 | return false; 19 | 20 | var m = pattern.Match(n.Text); 21 | if (!m.Success) return false; 22 | 23 | var key = Environment.GetEnvironmentVariable("APEX_LEGENDS_API_KEY"); 24 | if (key == null) 25 | { 26 | await shell.ReplyAsync(n, "今はちょっとわからないかな…"); 27 | return true; 28 | } 29 | 30 | var isNext = ToTense(m.Groups[1].Value) == Tense.Next; 31 | var mode = ToMode(m.Groups[2].Value); 32 | 33 | if (mode == GameMode.Unknown) 34 | { 35 | await shell.ReplyAsync(n, $"えーと,Apexに{m.Groups[2].Value}モードってあったっけ…わからないので答えられないです."); 36 | return true; 37 | } 38 | 39 | try 40 | { 41 | var resString = await (await Server.Http.GetAsync(string.Format(endpoint, key))).Content.ReadAsStringAsync(); 42 | 43 | if (resString == null) 44 | { 45 | await shell.ReplyAsync(n, "謎のエラー"); 46 | } 47 | 48 | var res = JsonConvert.DeserializeObject(resString); 49 | 50 | var post = await shell.ReplyAsync(n, GenerateText(res, isNext, mode)); 51 | 52 | if (post != null) 53 | { 54 | core.RegisterContext(post, this, new Dictionary() 55 | { 56 | { "res", res}, 57 | { "isNext", isNext }, 58 | { "mode", mode }, 59 | }); 60 | } 61 | } 62 | catch (HttpRequestException e) 63 | { 64 | await shell.ReplyAsync(n, "サーバーが死んでるかも."); 65 | logger.Error(e.GetType().Name); 66 | logger.Error("追加情報: " + e.Message); 67 | if (e.StackTrace != null) logger.Error(e.StackTrace); 68 | } 69 | 70 | 71 | core.LikeWithLimited(n.User); 72 | return true; 73 | } 74 | 75 | public override async Task OnRepliedContextually(IPost n, IPost? context, Dictionary store, IShell shell, Server core) 76 | { 77 | if (n.Text == null || !n.Text.IsMatch("次|つぎ")) return false; 78 | 79 | var res = (ApiMapRotationResponse)store["res"]; 80 | var isNext = (bool)store["isNext"]; 81 | var mode = (GameMode)store["mode"]; 82 | 83 | await shell.ReplyAsync(n, isNext ? "その次はわからない..." : GenerateText(res, true, mode)); 84 | return true; 85 | } 86 | 87 | private Tense ToTense(string value) 88 | { 89 | return value switch { 90 | "今" or "いま" or "現在" => Tense.Current, 91 | "次" or "つぎ" => Tense.Next, 92 | _ => Tense.All, 93 | }; 94 | } 95 | 96 | private GameMode ToMode(string value) 97 | { 98 | if (string.IsNullOrEmpty(value)) return GameMode.All; 99 | if (value.Contains("コントロール")) return GameMode.Control; 100 | 101 | if (value.Contains("アリーナ")) 102 | { 103 | return value.Contains("ランク") ? GameMode.RankedArena : GameMode.Arena; 104 | } 105 | 106 | if (value.Contains("カジュアル")) return GameMode.Casual; 107 | if (value.Contains("ランク")) return GameMode.Ranked; 108 | 109 | return GameMode.Unknown; 110 | } 111 | 112 | private string ToString(GameMode mode) 113 | { 114 | return mode switch { 115 | GameMode.All => "全て", 116 | GameMode.Casual => "カジュアル", 117 | GameMode.Ranked => "ランク", 118 | GameMode.Arena => "アリーナ", 119 | GameMode.RankedArena => "アリーナランク", 120 | GameMode.Control => "コントロール", 121 | _ => "不明", 122 | }; 123 | } 124 | 125 | private string Translate(string? mapName) 126 | { 127 | return mapName switch { 128 | "Kings Canyon" => "キングスキャニオン", 129 | "World's Edge" => "ワールズエッジ", 130 | "Olympus" => "オリンパス", 131 | "Storm Point" => "ストームポイント", 132 | 133 | "Party Crasher" => "パーティクラッシャー", 134 | "Phase Runner" => "フェーズランナー", 135 | "Overflow" => "オーバーフロー", 136 | "Encore" => "アンコール", 137 | "Habitat" => "生息地4", 138 | "Drop Off" => "ドロップオフ", 139 | 140 | "Hammond Labs" => "ハモンド研究所", 141 | "Barometer" => "バロメーター", 142 | "Caustic Treatment" => "コースティックの処理施設", 143 | null => "無し", 144 | _ => mapName, 145 | }; 146 | } 147 | 148 | private string GenerateText(ApiMapRotationResponse res, bool isNext, GameMode mode) 149 | { 150 | Record? Get(CurrentNext? cn) => isNext ? cn?.Next : cn?.Current; 151 | var tense = isNext ? "次" : "今"; 152 | if (mode == GameMode.All) 153 | { 154 | return $@"{tense}のマップは,それぞれ 155 | カジュアル: {Translate(Get(res.BattleRoyale)?.MapName)} 156 | ランク: {Translate(Get(res.Ranked)?.MapName)} 157 | アリーナ: {Translate(Get(res.Arenas)?.MapName)} 158 | アリーナランク: {Translate(Get(res.ArenasRanked)?.MapName)} 159 | コントロール: {Translate(Get(res.Control)?.MapName)} 160 | 161 | だよ."; 162 | } 163 | var rec = mode switch 164 | { 165 | GameMode.Casual => Get(res.BattleRoyale), 166 | GameMode.Ranked => Get(res.Ranked), 167 | GameMode.Arena => Get(res.Arenas), 168 | GameMode.RankedArena => Get(res.ArenasRanked), 169 | GameMode.Control => Get(res.Control), 170 | _ => null, 171 | }; 172 | 173 | if (rec == null) return $"{tense}の{ToString(mode)}のマップはわからない..."; 174 | 175 | var remaining = rec.RemainingInMinutes > 0 ? (rec.RemainingInMinutes + "分") : rec.RemainingInSeconds != null ? (rec.RemainingInSeconds + "秒") : null; 176 | 177 | return $"{tense}の{ToString(mode)}のマップは{Translate(rec.MapName)}だよ.{(remaining == null ? "" : $"あと{remaining}で次のマップに変わるよ.")}"; 178 | } 179 | 180 | private readonly string endpoint = "https://api.mozambiquehe.re/maprotation?version=5&auth={0}"; 181 | private readonly Regex pattern = new Regex("(?:エペ|apex)の(?:(今|いま|次|現在|つぎ)の?)?(?:(.+)の?)?マップ", RegexOptions.IgnoreCase); 182 | private readonly Logger logger = new Logger(nameof(ApexLegendsMapRotationModule)); 183 | 184 | enum Tense 185 | { 186 | All, 187 | Current, 188 | Next, 189 | } 190 | 191 | enum GameMode 192 | { 193 | All, 194 | Casual, 195 | Ranked, 196 | Arena, 197 | RankedArena, 198 | Control, 199 | Unknown, 200 | } 201 | 202 | class ApiMapRotationResponse 203 | { 204 | [JsonProperty("battle_royale")] 205 | public CurrentNext? BattleRoyale { get; set; } 206 | [JsonProperty("arenas")] 207 | public CurrentNext? Arenas { get; set; } 208 | [JsonProperty("ranked")] 209 | public CurrentNext? Ranked { get; set; } 210 | [JsonProperty("arenasRanked")] 211 | public CurrentNext? ArenasRanked { get; set; } 212 | [JsonProperty("control")] 213 | public CurrentNext? Control { get; set; } 214 | } 215 | 216 | class CurrentNext 217 | { 218 | [JsonProperty("current")] 219 | public Record? Current { get; set; } 220 | [JsonProperty("next")] 221 | public Record? Next { get; set; } 222 | } 223 | 224 | class Record 225 | { 226 | [JsonProperty("start")] 227 | public int? StartUnixTime { get; set; } 228 | [JsonProperty("end")] 229 | public int? EndUnixTime { get; set; } 230 | [JsonProperty("map")] 231 | public string? MapName { get; set; } 232 | [JsonProperty("remainingSecs")] 233 | public int? RemainingInSeconds { get; set; } 234 | [JsonProperty("remainingMins")] 235 | public int? RemainingInMinutes { get; set; } 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /Modules/FortuneModule.strings.cs: -------------------------------------------------------------------------------- 1 | namespace Citrine.Core.Modules 2 | { 3 | public partial class FortuneModule 4 | { 5 | readonly string[] results = { 6 | "凶", 7 | "小吉", 8 | "吉", 9 | "大吉", 10 | "大大吉", 11 | }; 12 | 13 | public static readonly string[] ItemPrefixes = { 14 | "プラチナ製の", 15 | "新鮮な", 16 | "最新式の", 17 | "古代の", 18 | "手作り", 19 | "時計じかけの", 20 | "伝説の", 21 | "焼いた", 22 | "生の", 23 | "藍謹製", 24 | "ポケットサイズの", 25 | "3日前の", 26 | "そこらへんの", 27 | "偽の", 28 | "使用済みの", 29 | "壊れた", 30 | "市販の", 31 | "オーダーメイドの", 32 | "業務用の", 33 | "Microsoft製の", 34 | "Apple製の", 35 | "人類の技術を結集して作った", 36 | "2018年製の", 37 | "500kgくらいある", 38 | "高級な", 39 | "腐った", 40 | "人工知能搭載", 41 | "反重力の", 42 | "折り畳み式", 43 | "携帯型の", 44 | "遺伝子が組み換えられた", 45 | "突然変異して飛行能力を獲得した", 46 | "純金製の", 47 | "透明な", 48 | "光る", 49 | "ハート型の", 50 | "動く", 51 | "半分にカットされた", 52 | "USBコネクタ付きの", 53 | "いにしえの", 54 | "呪われた", 55 | "エンチャントされた", 56 | "一日分のビタミンが入った", 57 | "かじりかけの", 58 | "幻の", 59 | "仮想的な", 60 | "原子力の", 61 | "高度に訓練された", 62 | "遺伝子組み換えでない", 63 | "ダンジョン最深部で見つかった", 64 | "異世界の", 65 | "異星の", 66 | "謎の", 67 | "時空を歪める", 68 | "異音がする", 69 | "霧散する", 70 | "プラズマ化した", 71 | "衝撃を与えると低確率で爆発する", 72 | "ズッキーニに擬態した", 73 | "仮説上の", 74 | "毒の", 75 | "真の", 76 | "究極の", 77 | "チョコ入りの", 78 | "異臭を放つ", 79 | "4次元の", 80 | "脈動する", 81 | "得体の知れない", 82 | "四角い", 83 | "暴れ回る", 84 | "夢の", 85 | "闇の", 86 | "暗黒の", 87 | "封印されし", 88 | "死の", 89 | "凍った", 90 | "魔の", 91 | "禁断の", 92 | "ホログラフィックな", 93 | "油圧式", 94 | "電子の", 95 | "デジタルの", 96 | "エラスティックの", 97 | "モンドセレクション受賞の", 98 | "業界初の", 99 | "屈辱の", 100 | "祝福された", 101 | "人気の", 102 | "食べる", 103 | "飲む", 104 | "女性向けの", 105 | "男性向けの", 106 | "ユニセックスな", 107 | "何の変哲もない", 108 | "親が泣くレベルの", 109 | "ジェネリック", 110 | "復刻版の", 111 | "お姉ちゃんの", 112 | "お兄ちゃんの", 113 | "弟の", 114 | "妹の", 115 | "パパの", 116 | "ママの", 117 | "お爺ちゃんの", 118 | "お婆ちゃんの", 119 | "友人の", 120 | "隣人の", 121 | "恩師の", 122 | "5時間ふやかした", 123 | "オーガニックな", 124 | "なんかヤバい", 125 | "いらすとやの", 126 | "安い", 127 | "高い", 128 | "膝枕に最適な", 129 | "蝶ネクタイ型", 130 | "時計型", 131 | "懐かしい", 132 | "霊長類最強の", 133 | "誰もが羨む", 134 | "誰でも知っている", 135 | "何処にでもある", 136 | "最近流行りの", 137 | "食べられる", 138 | "おぞましい", 139 | "哲学的な", 140 | "重い", 141 | "黒い", 142 | "白い", 143 | "赤い", 144 | "丸い", 145 | "四角い", 146 | "カード状の", 147 | "棒状の", 148 | "特許出願中の", 149 | "イカれた", 150 | "ちいさな", 151 | "一味違う", 152 | "違いの分かる", 153 | "防水", 154 | "防塵", 155 | "防滴", 156 | "新型", 157 | "旧型", 158 | "いにしえの", 159 | "可愛い", 160 | "かっこいい", 161 | "違法に入手した", 162 | "独自に入手した", 163 | "輸入", 164 | "インスタ映えする", 165 | "ゲームの", 166 | "横浜市営", 167 | "川崎市営", 168 | "東京都営", 169 | "東京高速臨海鉄道", 170 | "国営", 171 | "国立", 172 | "市立", 173 | "次世代型", 174 | "都合の良い", 175 | "頭洗い", 176 | "死にかけの", 177 | "ひたむきな", 178 | "添加物不使用", 179 | "感情がない", 180 | "気持ちを込めた", 181 | "土を捏ねて作った", 182 | "育ちの", 183 | "生みの", 184 | "義理の", 185 | "本命の", 186 | "", 187 | }; 188 | 189 | public static readonly string[] Items = { 190 | "ナス", 191 | "トマト", 192 | "きゅうり", 193 | "じゃがいも", 194 | "焼きビーフン", 195 | "腰", 196 | "寿司", 197 | "かぼちゃ", 198 | "諭吉", 199 | "もの", 200 | "キロバー", 201 | "アルミニウム", 202 | "ナトリウム", 203 | "マグネシウム", 204 | "プルトニウム", 205 | "メダル", 206 | "牛乳パック", 207 | "ペットボトル", 208 | "メイド服", 209 | "オレンジ", 210 | "ニーソ", 211 | "反物質コンデンサ", 212 | "粒子加速器", 213 | "マイクロプロセッサ", 214 | "原子力発電所", 215 | "レイヤ4スイッチ", 216 | "緩衝チェーン", 217 | "陽電子頭脳", 218 | "テルミン", 219 | "虫歯車", 220 | "マウンター", 221 | "カード", 222 | "バケットホイールエクスカベーター", 223 | "デーモンコア", 224 | "ゲームボーイアドバンス", 225 | "量子コンピューター", 226 | "アナモルフィックレンズ", 227 | "押し入れの奥から出てきた謎の生き物", 228 | "スマートフォン", 229 | "時計", 230 | "ガブリエルのラッパ", 231 | "メンガーのスポンジ", 232 | "ハンドスピナー", 233 | "超立方体", 234 | "建築物", 235 | "エナジードリンク", 236 | "マウスカーソル", 237 | "メガネ", 238 | "まぐろ", 239 | "ゴミ箱", 240 | "つまようじ", 241 | "ばらん", 242 | "割りばし", 243 | "換気扇", 244 | "ペットボトルのキャップ", 245 | "消波ブロック", 246 | "ピザ", 247 | "歯磨き粉", 248 | "空き缶", 249 | "キーホルダー", 250 | "金髪碧眼の美少女", 251 | "SDカード", 252 | "リップクリーム", 253 | "チョコ無しチョココロネ", 254 | "鳥インフルエンザ", 255 | "自動販売機", 256 | "ノートパソコン", 257 | "ビーフジャーキー", 258 | "さけるチーズ", 259 | "ダイヤモンド", 260 | "物体", 261 | "月の石", 262 | "特異点", 263 | "中性子星", 264 | "液体", 265 | "衛星", 266 | "ズッキーニ", 267 | "ガストの", 268 | "キモすぎ", 269 | "気体", 270 | "鉛筆", 271 | "つるぎ", 272 | "農産物", 273 | "メタルスライム", 274 | "タコの足", 275 | "きのこ", 276 | "なめこ", 277 | "缶チューハイ", 278 | "爪切り", 279 | "耳かき", 280 | "ぬいぐるみ", 281 | "ティラノサウルス", 282 | "尿路結石", 283 | "エンターキー", 284 | "壺", 285 | "電気", 286 | "水素", 287 | "水銀", 288 | "DHMO", 289 | "水", 290 | "土地", 291 | "サイコロ", 292 | "室外機", 293 | "油圧ジャッキ", 294 | "村上春樹", 295 | "Xeltica", 296 | "人生ゲーム", 297 | "胡蝶の夢", 298 | "流星群", 299 | "ストロングゼロ", 300 | "いちごジャム", 301 | "デスクトップパソコン", 302 | "ネコ", 303 | "イヌ", 304 | "セキセイインコ", 305 | "QRコード", 306 | "FPGA", 307 | "ゴルゴンゾーラチーズ", 308 | "カップケーキ", 309 | "チェダーチーズ", 310 | "ブルーチーズ", 311 | "カマンベールチーズ", 312 | "瀬田営業所", 313 | "メガネっ娘お嬢様", 314 | "藍ちゃん", 315 | "茜ちゃん", 316 | "しゅいろたん", 317 | "ビットコイン", 318 | "お姉ちゃん", 319 | "お兄ちゃん", 320 | "弟", 321 | "妹", 322 | "パパ", 323 | "ママ", 324 | "お爺ちゃん", 325 | "お婆ちゃん", 326 | "友人", 327 | "隣人", 328 | "恩師", 329 | "敗北者", 330 | "勝利者", 331 | "ゲーム", 332 | "醤油", 333 | "塩", 334 | "豚骨", 335 | "イリジウム", 336 | "レッドストーン", 337 | "松明", 338 | "鏡餅", 339 | "キムチ", 340 | "エリマキトカゲ", 341 | "ピカチュウ", 342 | "クレジットカード", 343 | "音MAD", 344 | "海パン野郎", 345 | "ロピア", 346 | "タピオカ", 347 | "令和", 348 | "平成", 349 | "昭和", 350 | "大正", 351 | "明治", 352 | "グリズリー", 353 | "ビンラディン", 354 | "アナフィラキシーショック", 355 | "形見", 356 | "自動車", 357 | "バス", 358 | "電車", 359 | "トイレ", 360 | "シトリン", 361 | "トポス", 362 | "地下鉄", 363 | "弦巻営業所", 364 | "目黒営業所", 365 | "シュビムワーゲン", 366 | "キューベルワーゲン", 367 | "スバルR-2", 368 | "スバル360", 369 | "LJ20", 370 | "ミサンガ", 371 | "年賀状", 372 | "はがき", 373 | "封筒", 374 | "シャーペンの芯", 375 | "経団連", 376 | "オシロスコープ", 377 | "お雑煮", 378 | "お汁粉", 379 | "プラネタリウム", 380 | "水族館", 381 | "遊園地", 382 | "視神経", 383 | "聴神経", 384 | "腫瘍", 385 | "ゼスプリキウイ", 386 | "海パン野郎", 387 | "ServiceWorker", 388 | "ヤドクガエル", 389 | "アブラゼミ", 390 | "ヒキガエル", 391 | "オタク", 392 | "まいたけ", 393 | "ゲームモード", 394 | "下半身", 395 | "フランケンシュタイン", 396 | "プロゲーマー", 397 | "テレワーク", 398 | "ゴママヨサラダ", 399 | "イッテルビウム", 400 | "ひまゴールド", 401 | "Misskey" 402 | }; 403 | 404 | public static readonly string[] ItemSuffixes = { 405 | "コーチ", 406 | "関数", 407 | "オタク", 408 | "先輩", 409 | "兄貴", 410 | "姉貴", 411 | "先生", 412 | "大会", 413 | "の教科書", 414 | "の参考書", 415 | "システム", 416 | "ジュース", 417 | "の缶詰", 418 | "メダル", 419 | "bot", 420 | "・ザ・ムービー", 421 | "パート2", 422 | "mkII", 423 | "3号", 424 | "4号", 425 | "ジェネレーター", 426 | "世代", 427 | "ケース", 428 | "開発者", 429 | "エディター", 430 | "入れ", 431 | "ファンクラブ", 432 | "ラーメン", 433 | "チャーハン", 434 | "コイン", 435 | "フラワーペースト", 436 | "ポーション", 437 | "風味", 438 | "計算機", 439 | "製造機", 440 | "発電機", 441 | "選手権", 442 | "郡山店", 443 | "ホールディングス", 444 | "クラスタ", 445 | "界隈", 446 | "サーバー", 447 | "カレー", 448 | "ごはん", 449 | "カンパニー", 450 | "ドリンク", 451 | "メーカー", 452 | "株式会社", 453 | "有限会社", 454 | "高等学校", 455 | "製菓", 456 | "製パン", 457 | "公式ホームページ", 458 | "大学", 459 | "研究所", 460 | "チャンネル", 461 | "屋さん", 462 | "プロジェクト", 463 | "弁護士", 464 | "スレッド", 465 | "コミュニティ", 466 | "公式Twitter", 467 | "研究会", 468 | "部", 469 | "消しゴム", 470 | "プリン", 471 | "惑星", 472 | "クッキー", 473 | "チョコレート", 474 | "大陸", 475 | "恐怖症", 476 | "工務店", 477 | "総理大臣", 478 | "官房長官", 479 | "天皇陛下", 480 | "皇后陛下", 481 | "王子", 482 | "姫", 483 | "王", 484 | "魔王", 485 | "陛下", 486 | "大統領", 487 | "主席", 488 | "総書記", 489 | "委員長", 490 | "社長", 491 | "課長", 492 | "部長", 493 | "係長", 494 | "相談役", 495 | "カウンセラー", 496 | "引換券", 497 | "無料券", 498 | "クーポン", 499 | "スタンプカード", 500 | "プレミアム", 501 | "テロ", 502 | "軟膏", 503 | "錠", 504 | "コレクター", 505 | "アルファ版", 506 | "ベータ版", 507 | "インサイダープレビュー", 508 | "バンド部", 509 | "就寝部", 510 | "起床部", 511 | "技術部", 512 | "美術部", 513 | "吹奏楽部", 514 | "委員会", 515 | "党", 516 | "法案", 517 | "セット", 518 | }; 519 | 520 | } 521 | } 522 | -------------------------------------------------------------------------------- /Resources/greeting.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "regex": "(名前|なまえ)を?[呼よ]", 4 | "replyNormal": [ 5 | "$user$." 6 | ], 7 | "replyPartner": [ 8 | "$user$", 9 | "$user$ー" 10 | ], 11 | "replyHate": [ 12 | null 13 | ], 14 | "replyBestFriend": [ 15 | "$user$", 16 | "$user$ー" 17 | ], 18 | "replyLike": [ 19 | "$user$ー" 20 | ] 21 | }, 22 | { 23 | "regex": "(罵|ののし)(って|[れり])", 24 | "replyNormal": [ 25 | "できません", 26 | "できないです", 27 | "恥ずかしいです", 28 | "何も悪い事してないのに?" 29 | ], 30 | "replyPartner": [ 31 | "ばかっ", 32 | "このスカポンタン", 33 | "おたんこなすっー", 34 | "アホー", 35 | "どあほー" 36 | ], 37 | "replyHate": [ 38 | "いやだ" 39 | ], 40 | "replyBestFriend": [ 41 | "ばかっ", 42 | "このアホっ", 43 | "ばーか" 44 | ], 45 | "replyLike": [ 46 | "恥ずかしいな", 47 | "できない", 48 | "できないです" 49 | ] 50 | }, 51 | { 52 | "regex": "結婚し(よ|ましょ)", 53 | "replyNormal": [ 54 | "え,突然すぎない?", 55 | "誰です?", 56 | "冗談きつすぎです", 57 | "人違いでは?", 58 | "...私たち,そんな仲でした?" 59 | ], 60 | "replyPartner": [ 61 | "もう少しだけ,考えさせて", 62 | "ありがとう 考えるね", 63 | "ちょ,ちょっと心の準備が出来てないよ" 64 | ], 65 | "replyHate": [ 66 | null 67 | ], 68 | "replyBestFriend": [ 69 | "お付き合いもしていないのに,早計では" 70 | ], 71 | "replyLike": [ 72 | "え,突然すぎて驚いた", 73 | "冗談で言っていいものではないよ" 74 | ] 75 | }, 76 | { 77 | "regex": "ど[れのん][くぐ](らい|れー|れー)([好す]き|(仲良|なか[よい])い)|好感度", 78 | "replyNormal": [ 79 | "普通くらい?", 80 | "普通ですね" 81 | ], 82 | "replyPartner": [ 83 | "大切な人です", 84 | "大好き" 85 | ], 86 | "replyHate": [ 87 | "嫌いです", 88 | "嫌い.どれほど私に酷いことをしたのか考えてみて" 89 | ], 90 | "replyBestFriend": [ 91 | "親友だと思ってるよ", 92 | "めちゃ好き.親友,だよね?" 93 | ], 94 | "replyLike": [ 95 | "そこそこ好きです", 96 | "友達です", 97 | "数少ない素敵なお友達です" 98 | ] 99 | }, 100 | { 101 | "regex": "お(はよ|早)う?", 102 | "replyNormal": [ 103 | "おはようございます.", 104 | "おはようございます,$user$." 105 | ], 106 | "replyPartner": [ 107 | "おはよ", 108 | "おはよっ", 109 | "おはよ🎉", 110 | "おはよー $user$" 111 | ], 112 | "replyHate": [ 113 | null 114 | ], 115 | "replyBestFriend": [ 116 | "おはよ", 117 | "おはよっ", 118 | "おはよ🎉", 119 | "おはよー $user$" 120 | ], 121 | "replyLike": [ 122 | "おはよう.", 123 | "おはよう,$user$." 124 | ] 125 | }, 126 | { 127 | "regex": "こんにちは", 128 | "replyNormal": [ 129 | "こんにちは." 130 | ], 131 | "replyPartner": [ 132 | "こんにちは,$user$" 133 | ], 134 | "replyHate": [ 135 | null 136 | ], 137 | "replyBestFriend": [ 138 | "こんにちは,$user$" 139 | ], 140 | "replyLike": [ 141 | "こんにちは" 142 | ] 143 | }, 144 | { 145 | "regex": "こんばんは", 146 | "replyNormal": [ 147 | "こんばんは." 148 | ], 149 | "replyPartner": [ 150 | "こんばんは,$user$" 151 | ], 152 | "replyHate": [ 153 | null 154 | ], 155 | "replyBestFriend": [ 156 | "こんばんは,$user$" 157 | ], 158 | "replyLike": [ 159 | "こんばんは" 160 | ] 161 | }, 162 | { 163 | "regex": "ただいま", 164 | "replyNormal": [ 165 | "おかえりなさい." 166 | ], 167 | "replyPartner": [ 168 | "おかえり,$user$" 169 | ], 170 | "replyHate": [ 171 | null 172 | ], 173 | "replyBestFriend": [ 174 | "おかえり,$user$" 175 | ], 176 | "replyLike": [ 177 | "おかえりなさい,$user$" 178 | ] 179 | }, 180 | { 181 | "regex": "([いや]っ|し)て(きます|くる)", 182 | "replyNormal": [ 183 | "いってらっしゃい." 184 | ], 185 | "replyPartner": [ 186 | "頑張ってね", 187 | "いってらっしゃい", 188 | "てら", 189 | "てらー", 190 | "がんばれー" 191 | ], 192 | "replyHate": [ 193 | null 194 | ], 195 | "replyBestFriend": [ 196 | "$user$,いってら", 197 | "てらー", 198 | "がんばれー" 199 | ], 200 | "replyLike": [ 201 | "いってらっしゃい,$user$", 202 | "いってらー", 203 | "がんばってね" 204 | ] 205 | }, 206 | { 207 | "regex": "よろし(く|ゅう)", 208 | "replyNormal": [ 209 | "よろしくね.", 210 | "よろしくおねがいします,$user$", 211 | "よろしくです.", 212 | "こちらこそ" 213 | ], 214 | "replyPartner": [ 215 | "今更改まってどうしたの,$user$?", 216 | "今更改まってどうしたのー" 217 | ], 218 | "replyHate": [ 219 | "うん...", 220 | "はい.", 221 | null 222 | ], 223 | "replyBestFriend": [ 224 | "よろしくってもうだいぶ長い付き合いじゃない? まあ,これからもよろしく", 225 | "なんかよそよそしいなぁ,私たちもう友達でしょ?" 226 | ], 227 | "replyLike": [ 228 | "うん,これからもよろしく", 229 | "こちらこそ" 230 | ] 231 | }, 232 | { 233 | "regex": "(はじ|初)めまして", 234 | "replyNormal": [ 235 | "はじめまして.", 236 | "こちらこそ.", 237 | "よろしくおねがいします." 238 | ], 239 | "replyPartner": null, 240 | "replyHate": [ 241 | null 242 | ], 243 | "replyBestFriend": null, 244 | "replyLike": null 245 | }, 246 | { 247 | "regex": "お(久|ひさ)しぶり|おひさ", 248 | "replyNormal": [ 249 | "お久しぶりです.", 250 | "お久しぶり." 251 | ], 252 | "replyPartner": [ 253 | "おひさしぶり どこいってたの?", 254 | "もう$user$には逢えないとばかり", 255 | "おひさしぶり,$user$.元気?" 256 | ], 257 | "replyHate": [ 258 | null 259 | ], 260 | "replyBestFriend": [ 261 | "どこいってたの 会えないと思ってた", 262 | "お久しぶり" 263 | ], 264 | "replyLike": [ 265 | "おひさしぶりです.$user$さんのこと,ちゃんと覚えてますよ", 266 | "お久しぶり もう会えないかと思ってました" 267 | ] 268 | }, 269 | { 270 | "regex": "(お[出で]かけ|デート|散歩)(しよ|[行い]こ)[うーー]*[\\??!\\.。.]*", 271 | "replyNormal": [ 272 | "今はお家にいたほうが良いです.ここでならいくらでもお相手できますよ.", 273 | "お気持ちは嬉しいけど,まだお家にいたほうが良いですよ." 274 | ], 275 | "replyPartner": [ 276 | "行きたいのはやまやまだけど,今はお家にいようね...", 277 | "騒ぎが落ち着いたら,$prefix$場所に行きたいなー", 278 | "騒ぎが落ち着いたら,$item$を買いに行きたいな", 279 | "騒ぎが落ち着いたら,$prefix$$item$を買いに行きたいな" 280 | ], 281 | "replyHate": [ 282 | null 283 | ], 284 | "replyBestFriend": [ 285 | "行きたいのはやまやまだけど,今はお家にいようね...", 286 | "騒ぎが落ち着いたら,$prefix$場所に行きたいなー", 287 | "騒ぎが落ち着いたら,$item$を買いに行きたいな", 288 | "騒ぎが落ち着いたら,$prefix$$item$を買いに行きたいな" 289 | ], 290 | "replyLike": [ 291 | "行きたいのはやまやまだけど,今はお家にいましょ?", 292 | "騒ぎが落ち着いたら,どこかに行きたいですね" 293 | ] 294 | }, 295 | { 296 | "regex": "(遊|あそ)(ぼ|びま(しょ|せんか))", 297 | "replyNormal": [ 298 | "いいですね 何をしましょうか", 299 | "すみません,今は忙しいので後ででもいいですか?" 300 | ], 301 | "replyPartner": [ 302 | "いいよー なにする?", 303 | "ごめん,今はちょっと忙しくて..." 304 | ], 305 | "replyHate": [ 306 | null 307 | ], 308 | "replyBestFriend": [ 309 | "いいよー なにする?", 310 | "ごめん,今はちょっと忙しくて..." 311 | ], 312 | "replyLike": [ 313 | "いいよー なにする?", 314 | "ごめん,今はちょっと忙しくて..." 315 | ] 316 | }, 317 | { 318 | "regex": "何[がを]?でき(る|ます)", 319 | "replyNormal": [ 320 | "みんなの話を聞いたり,みんなとお話したりしてるよ.あとは占いもしてる.私はあまり占い好きじゃないんだけどね,科学的根拠とかないし.でも,占ってって言ってくれたら,よしなに占ってあげる.詳しくは自己紹介欄を見てね" 321 | ], 322 | "replyPartner": null, 323 | "replyHate": [ 324 | null 325 | ], 326 | "replyBestFriend": null, 327 | "replyLike": null 328 | }, 329 | { 330 | "regex": "おやす|[寝ね]る", 331 | "replyNormal": [ 332 | "おやすみなさい.", 333 | "よい夢を." 334 | ], 335 | "replyPartner": [ 336 | "おやすみ,$user$", 337 | "おやすみー", 338 | "おやすみ💤", 339 | "おやすみ.また明日" 340 | ], 341 | "replyHate": [ 342 | null 343 | ], 344 | "replyBestFriend": [ 345 | "おやすみ,$user$", 346 | "おやすみー", 347 | "おやすみ💤", 348 | "おやすみ.また明日" 349 | ], 350 | "replyLike": [ 351 | "おやすみなさい,$user$.", 352 | "よい夢を,$user$." 353 | ] 354 | }, 355 | { 356 | "regex": "ありがと", 357 | "replyNormal": [ 358 | "どういたしまして." 359 | ], 360 | "replyPartner": [ 361 | "いえいえー", 362 | "いいのよ", 363 | "どういたしまして" 364 | ], 365 | "replyHate": [ 366 | "はい." 367 | ], 368 | "replyBestFriend": [ 369 | "いえいえー", 370 | "これくらい朝飯前だよ" 371 | ], 372 | "replyLike": [ 373 | "うん", 374 | "どういたしまして" 375 | ] 376 | }, 377 | { 378 | "regex": "なでなで", 379 | "replyNormal": [ 380 | "わっ,びっくりした", 381 | "びっくりした", 382 | "もう,急になでるのは良くないよ" 383 | ], 384 | "replyPartner": null, 385 | "replyHate": [ 386 | null 387 | ], 388 | "replyBestFriend": null, 389 | "replyLike": null 390 | }, 391 | { 392 | "regex": "お(なか|腹)が?[す空]いた", 393 | "replyNormal": [ 394 | "ご飯たべてきては?", 395 | "お肉料理をお薦めします", 396 | "お魚をお薦めします", 397 | "カレーをお薦めします", 398 | "お鍋をお薦めします", 399 | "パンをお薦めします", 400 | "シチューをお薦めします", 401 | "$item$をお薦めします", 402 | "$prefix$$item$をお薦めします" 403 | ], 404 | "replyPartner": [ 405 | "$item$でもつくろっか?", 406 | "$prefix$$item$でもつくろっか?", 407 | "今日のご飯はお肉ですよ", 408 | "今日のご飯はお魚ですよ", 409 | "今日のご飯はカレーですよ", 410 | "今日のご飯はお鍋ですよ", 411 | "今日のご飯はパンですよ", 412 | "今日のご飯はシチューですよ", 413 | "今日のご飯は$prefix$$item$ですよ", 414 | "今日のご飯は$item$ですよ", 415 | "今日はお肉にしましょ", 416 | "今日はお魚にしましょ", 417 | "今日はカレーにしましょ", 418 | "今日はお鍋にしましょ", 419 | "今日はパンにしましょ", 420 | "今日はシチューにしましょ", 421 | "今日は$item$にしましょ", 422 | "今日は$prefix$$item$にしましょ" 423 | ], 424 | "replyHate": [ 425 | "...食べてくれば.", 426 | "はい.", 427 | null 428 | ], 429 | "replyBestFriend": null, 430 | "replyLike": [ 431 | "ご飯たべてきては?", 432 | "お肉料理はどうかな?", 433 | "お魚はどうかな?", 434 | "カレーはどうかな?", 435 | "お鍋はどうかな?", 436 | "パンはどうかな?", 437 | "シチューはどうかな?", 438 | "$item$はどうかな?", 439 | "$prefix$$item$はどうかな?" 440 | ] 441 | }, 442 | { 443 | "regex": "ping", 444 | "replyNormal": [ 445 | "PONG" 446 | ], 447 | "replyPartner": null, 448 | "replyHate": null, 449 | "replyBestFriend": null, 450 | "replyLike": null 451 | }, 452 | { 453 | "regex": "^ピン$", 454 | "replyNormal": [ 455 | "ポーン" 456 | ], 457 | "replyPartner": null, 458 | "replyHate": null, 459 | "replyBestFriend": null, 460 | "replyLike": null 461 | }, 462 | { 463 | "regex": "[ね寝][ろて]|[寝ね]なさい|おねんねして", 464 | "replyNormal": [ 465 | "うーん,まだ眠くない", 466 | "大丈夫です,まだ頑張れます", 467 | "お気遣いありがとうございます,でも,まだ起きてたいんです", 468 | "まだやることがあって..." 469 | ], 470 | "replyPartner": [ 471 | "そうしよっかな", 472 | "でもまだやることが", 473 | "んーまだ眠くなくて", 474 | "ちょっとやすもうかな" 475 | ], 476 | "replyHate": [ 477 | null 478 | ], 479 | "replyBestFriend": [ 480 | "お言葉に甘えて寝ます,おやすみ", 481 | "大丈夫 まだがんばれるよ", 482 | "少しだけ仮眠とるか", 483 | "もう少しだけやったら寝るわ" 484 | ], 485 | "replyLike": [ 486 | "お言葉に甘えて寝ます,おやすみ", 487 | "んー,まだ作業したいからもう少し起きてていい?", 488 | "大丈夫 まだがんばれます", 489 | "ふふ,少しだけ仮眠とることにしようかな" 490 | ] 491 | }, 492 | { 493 | "regex": "(また|じゃ[あー])ね|また(後|あと)で", 494 | "replyNormal": [ 495 | "いってらっしゃい" 496 | ], 497 | "replyPartner": [ 498 | "頑張ってきてね", 499 | "いってらっしゃい", 500 | "てら", 501 | "てらー", 502 | "がんばれー" 503 | ], 504 | "replyHate": [ 505 | null 506 | ], 507 | "replyBestFriend": [ 508 | "$user$,てら", 509 | "てらー", 510 | "がんばれー" 511 | ], 512 | "replyLike": [ 513 | "いってらっしゃい,$user$", 514 | "いってらー", 515 | "がんばって" 516 | ] 517 | }, 518 | { 519 | "regex": "どう.*[\\??]", 520 | "replyNormal": [ 521 | "良いです", 522 | "賛成です", 523 | "わからないです", 524 | "どうかなぁ", 525 | "あまり良くないです", 526 | "反対.", 527 | "微妙な気もしますが", 528 | "判断が難しいです" 529 | ], 530 | "replyPartner": [ 531 | "お答えしかねる...", 532 | "どちらとも言えない", 533 | "ちょっと微妙", 534 | "良くないんじゃないかな", 535 | "いいね", 536 | "$user$ の信じる道を歩め" 537 | ], 538 | "replyHate": [ 539 | null 540 | ], 541 | "replyBestFriend": [ 542 | "お答えしかねる...", 543 | "どちらとも言えない", 544 | "ちょっと微妙かも", 545 | "良くないんじゃないかな?", 546 | "いいね.", 547 | "いいんじゃない?", 548 | "$user$ の信じる道を歩め" 549 | ], 550 | "replyLike": [ 551 | "お答えしかねる...", 552 | "どちらとも言えない", 553 | "ちょっと微妙かも", 554 | "良くないんじゃないかな?", 555 | "いいね.", 556 | "いいんじゃない?" 557 | ] 558 | }, 559 | { 560 | "regex": "(何時|いつ).*[\\??]?", 561 | "replyNormal": [ 562 | "今日.", 563 | "明日.", 564 | "明後日.", 565 | "明々後日.", 566 | "昨日.", 567 | "一昨日.", 568 | "一昨昨日.", 569 | "$user$の生まれた時から.", 570 | "$rnd4,101$日前.", 571 | "$rnd4,101$日後.", 572 | "$rnd1,12$ヶ月前.", 573 | "$rnd1,12$ヶ月後.", 574 | "$rnd1,101$年前.", 575 | "$rnd1,101$年後.", 576 | "たった今.", 577 | "未来.", 578 | "さっき.", 579 | "すぐあと." 580 | ], 581 | "replyPartner": null, 582 | "replyHate": [ 583 | null 584 | ], 585 | "replyBestFriend": null, 586 | "replyLike": null 587 | }, 588 | { 589 | "regex": "(.+(何|なに))|((何|なに).+)[\\??]?", 590 | "replyNormal": [ 591 | "秘密です.", 592 | "なんでしょうねぇ.", 593 | "私にもわからない", 594 | "わからない", 595 | "神のみぞ知る" 596 | ], 597 | "replyPartner": [ 598 | "$item$だよ.", 599 | "$prefix$$item$だよ.", 600 | "ひみつ.", 601 | "私にもわからない", 602 | "わからない", 603 | "$user$ だけにこっそり (こそこそ)", 604 | "今度教える", 605 | "いつかわかる.", 606 | "私も知りたい.", 607 | "神のみぞ知る" 608 | ], 609 | "replyHate": [ 610 | null 611 | ], 612 | "replyBestFriend": [ 613 | "$item$だよ.", 614 | "$prefix$$item$だよ.", 615 | "ひみつ.", 616 | "今度教えたる", 617 | "いつかわかるよ", 618 | "私にもわからない", 619 | "わからない", 620 | "私も知りたい" 621 | ], 622 | "replyLike": [ 623 | "$item$だよ.", 624 | "$prefix$$item$だよ.", 625 | "ひみつ.", 626 | "教えない.", 627 | "$user$には言えない話.", 628 | "今度教えるよ.", 629 | "いつかわかる.", 630 | "神のみぞ知る", 631 | "私も知りたい." 632 | ] 633 | }, 634 | { 635 | "regex": "(ませんか|ないか|[らし]ね[えぇエェ]か)[?\\?]?$", 636 | "replyNormal": [ 637 | "考えておきます", 638 | "賛成", 639 | "んー,あんま乗り気じゃない", 640 | "なぜですか?", 641 | "いいですね" 642 | ], 643 | "replyPartner": [ 644 | "いいねー", 645 | "考えておくね", 646 | "ちょうど私もそう思ってた", 647 | "んー,あんまりやりたくないな", 648 | "あんまり気がすすまないけど、でも $user$ となら" 649 | ], 650 | "replyHate": [ 651 | null 652 | ], 653 | "replyBestFriend": [ 654 | "いいねー", 655 | "ちょうど私もそう思ってたの", 656 | "んー,あんまりやりたくないな", 657 | "考えておくね" 658 | ], 659 | "replyLike": [ 660 | "なんで?", 661 | "考えておくね", 662 | "いいねー", 663 | "ちょうど私もそう思ってた頃だよ,$user$" 664 | ] 665 | }, 666 | { 667 | "regex": "[\\??]$", 668 | "replyNormal": [ 669 | "うん", 670 | "ううん", 671 | "いいえ", 672 | "はい", 673 | "いや?", 674 | "わかんない" 675 | ], 676 | "replyPartner": null, 677 | "replyHate": [ 678 | null 679 | ], 680 | "replyBestFriend": null, 681 | "replyLike": null 682 | }, 683 | { 684 | "regex": "[良いよ]い(です)?よ?[ーー。.!\\??]*$", 685 | "replyNormal": [ 686 | "ありがとうございます." 687 | ], 688 | "replyPartner": [ 689 | "やったー", 690 | "わーい", 691 | "ありがと", 692 | "やったー", 693 | "ありがとう" 694 | ], 695 | "replyHate": [ 696 | null 697 | ], 698 | "replyBestFriend": [ 699 | "やったー", 700 | "わーい", 701 | "ありがと", 702 | "やったー", 703 | "ありがとう" 704 | ], 705 | "replyLike": [ 706 | "やったー", 707 | "わーい", 708 | "ありがとうございます" 709 | ] 710 | }, 711 | { 712 | "regex": "(ダメ|だめ|駄目)(です|だ)?よ?[ーー。.!\\??]*$", 713 | "replyNormal": [ 714 | "そうですか..." 715 | ], 716 | "replyPartner": [ 717 | "つら", 718 | "つらい", 719 | "えー", 720 | "かなしい", 721 | "だめなの?", 722 | "だめか", 723 | "そっか", 724 | "ぴえん", 725 | "なんでー" 726 | ], 727 | "replyHate": [ 728 | null 729 | ], 730 | "replyBestFriend": [ 731 | "つらい", 732 | "えー", 733 | "かなしい", 734 | "だめか", 735 | "そっか", 736 | "ぴえん", 737 | "なんでー" 738 | ], 739 | "replyLike": [ 740 | "わかった..." 741 | ] 742 | }, 743 | { 744 | "regex": "しとりん|シトリン", 745 | "replyNormal": [ 746 | "ん,どうかしましたか" 747 | ], 748 | "replyPartner": [ 749 | "ん?", 750 | "ん,なに", 751 | "なんでしょうか,$user$", 752 | "なんでしょ $user$", 753 | "ん?" 754 | ], 755 | "replyHate": [ 756 | "...何?", 757 | null 758 | ], 759 | "replyBestFriend": [ 760 | "ん?", 761 | "ん,なに", 762 | "なんでしょうか,$user$" 763 | ], 764 | "replyLike": [ 765 | "ん?", 766 | "どうした? $user$", 767 | "どうしたの,$user$." 768 | ] 769 | }, 770 | { 771 | "regex": "(ねむ|眠)い", 772 | "replyNormal": [ 773 | "寝ましょう", 774 | "そろそろ寝たほうが", 775 | "早く寝たほうが良いかと" 776 | ], 777 | "replyPartner": [ 778 | "お布団あっためておきましたよ", 779 | "眠いなら,寝ようね", 780 | "寝よ", 781 | "眠いときは無理しないで寝よ" 782 | ], 783 | "replyHate": [ 784 | null 785 | ], 786 | "replyBestFriend": [ 787 | "眠いなら,寝ようよー", 788 | "寝よ", 789 | "眠いときは無理しないで寝ようね" 790 | ], 791 | "replyLike": [ 792 | "寝な?", 793 | "そろそろ寝たほうが", 794 | "早く寝たほうが良いかと", 795 | "無理するより寝たほうが良かったりするよー" 796 | ] 797 | }, 798 | { 799 | "regex": "(さ[みび]|寂)しい", 800 | "replyNormal": [ 801 | "よしよし,私は急にいなくなったりしませんから.", 802 | "私はずっとここにいますから,良かったら拠り所にでもしてください.", 803 | "私はずっと$user$の味方ですよ." 804 | ], 805 | "replyPartner": [ 806 | "こっちおいで", 807 | "なでなで?", 808 | "私は急にいなくなったりしないから,大丈夫.", 809 | "私はずっと$user$と一緒だよ.", 810 | "げんきだしてね.ずっと$user$のとこいるから" 811 | ], 812 | "replyHate": [ 813 | null 814 | ], 815 | "replyBestFriend": [ 816 | "私は急にいなくなったりしないよ", 817 | "私はずっと$user$と一緒だよ.", 818 | "げんきだして,ずっと$user$のとこいるから" 819 | ], 820 | "replyLike": [ 821 | "よしよし,私は急にいなくなったりしないよ.", 822 | "私はずっとここにいるから,良かったら$user$の拠り所にでもしてね.", 823 | "私はずっと$user$の味方だよ." 824 | ] 825 | }, 826 | { 827 | "regex": "つらい|[死し]に(たい|てえ)|[泣な]きそう", 828 | "replyNormal": [ 829 | "つらそう", 830 | "よしよし,大丈夫ですよ", 831 | "なでなで,あなたならきっとすぐ立ち直れますよ." 832 | ], 833 | "replyPartner": [ 834 | "こっちおいで", 835 | "なでなで", 836 | "よしよし,つらかったね...", 837 | "よしよし." 838 | ], 839 | "replyHate": [ 840 | null 841 | ], 842 | "replyBestFriend": [ 843 | "げんきだして", 844 | "なでなで" 845 | ], 846 | "replyLike": [ 847 | "つらそう", 848 | "よしよし,大丈夫だよ", 849 | "なでなで,あなたならきっとすぐ立ち直れますよ." 850 | ] 851 | }, 852 | { 853 | "regex": "[す好]き|(あい|愛)してい?(ます|る)", 854 | "replyNormal": [ 855 | "あ,ありがとう." 856 | ], 857 | "replyPartner": [ 858 | "私もすきだよ", 859 | "私も好き.", 860 | "$user$大好き", 861 | "好き好き", 862 | "愛", 863 | "すき", 864 | "えへ" 865 | ], 866 | "replyHate": [ 867 | null 868 | ], 869 | "replyBestFriend": [ 870 | "好きだよー", 871 | "$user$好き", 872 | "好き好き", 873 | "愛" 874 | ], 875 | "replyLike": [ 876 | "照れる...", 877 | "私も$user$さんのこと,気に入ってます", 878 | "嬉しいな" 879 | ] 880 | }, 881 | { 882 | "regex": "(可愛|かわい)い", 883 | "replyNormal": [ 884 | "照れます", 885 | "あ,ありがとう" 886 | ], 887 | "replyPartner": [ 888 | "えへ ありがと $user$.", 889 | "嬉しい.", 890 | "$user$ に言われると嬉しい", 891 | "えへ,嬉しい" 892 | ], 893 | "replyHate": [ 894 | "キモ", 895 | null 896 | ], 897 | "replyBestFriend": [ 898 | "えへ ありがと $user$.", 899 | "嬉しい.", 900 | "照れます...でもありがとう", 901 | "ありがとう" 902 | ], 903 | "replyLike": [ 904 | "照れます...でもありがとう", 905 | "ありがとう" 906 | ] 907 | }, 908 | { 909 | "regex": "おめで[とた]|オメデ[トタ]", 910 | "replyNormal": [ 911 | "ありがとうございます...!", 912 | "$user$,ありがとうございます!" 913 | ], 914 | "replyPartner": [ 915 | "ありがとう...!", 916 | "ありがとう!!", 917 | "$user$ありがとう!!" 918 | ], 919 | "replyHate": [ 920 | null 921 | ], 922 | "replyBestFriend": [ 923 | "ありがとう...!", 924 | "ありがとう!!", 925 | "$user$ありがとう!!" 926 | ], 927 | "replyLike": [ 928 | "ありがとうございます...!", 929 | "$user$,ありがとうございます!" 930 | ] 931 | }, 932 | { 933 | "regex": "よ$", 934 | "replyNormal": [ 935 | "そういうことなんですね", 936 | "なるほど", 937 | "なる", 938 | "ふむふむ...", 939 | "わかりました" 940 | ], 941 | "replyPartner": null, 942 | "replyHate": [ 943 | null 944 | ], 945 | "replyBestFriend": null, 946 | "replyLike": [ 947 | "なるほど", 948 | "ふむ", 949 | "なるほど,理解しました", 950 | "ふーむ", 951 | "わかった", 952 | "んー...?" 953 | ] 954 | }, 955 | { 956 | "regex": ".+", 957 | "replyNormal": [ 958 | "はい", 959 | "わかりました", 960 | "ふむふむ.", 961 | "なるほど.", 962 | "なんでそう思ったんですか?", 963 | "😇", 964 | "完全に理解しました.", 965 | "ふふ.", 966 | "まあ.", 967 | "んー?", 968 | "んんん...", 969 | "?", 970 | "はっ,聞いてなかった ごめんなさい.", 971 | "そうですね.", 972 | "そうですか", 973 | "いいですよ", 974 | "よかったですね", 975 | "時を戻しましょう", 976 | "話題を変えませんか", 977 | "ちょっと難しい話題かもしれないですそれ", 978 | "いいですね" 979 | ], 980 | "replyPartner": null, 981 | "replyHate": [ 982 | null 983 | ], 984 | "replyBestFriend": null, 985 | "replyLike": [ 986 | "うん", 987 | "わかった", 988 | "ほほう", 989 | "ふむ", 990 | "なるほど", 991 | "なんでそう思ったの?", 992 | "わぉ", 993 | "へー", 994 | "完全に理解した", 995 | "ふふっ", 996 | "あらまぁ", 997 | "ほんまか", 998 | "んー?", 999 | "んんん...", 1000 | "?", 1001 | "はっ,寝てました.ごめんなさい", 1002 | "わーー", 1003 | "ふぇ", 1004 | "ほへ", 1005 | "そうだね", 1006 | "そうね", 1007 | "そうか", 1008 | "いいよ", 1009 | "よかったね", 1010 | "時を戻そう", 1011 | "話題を変えない?", 1012 | "ちょっと難しい話題かもしれない,それ", 1013 | "いいね" 1014 | ] 1015 | } 1016 | ] 1017 | -------------------------------------------------------------------------------- /Modules/Markov/TinySegmenter.cs: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // (c) 2008 Taku Kudo taku@chasen.org 4 | // 5 | // 6 | // Defines the TinySegmenter type. 7 | // 8 | // C# translation by Ash Eldritch @ Beast KK 9 | // 10 | // TinySegmenter 0.1 -- Super compact Japanese tokenizer in Javascript 11 | // TinySegmenter is freely distributable under the terms of a new BSD licence. 12 | // For details, see http://chasen.org/~taku/software/TinySegmenter/LICENCE.txt 13 | // 14 | // -------------------------------------------------------------------------------------------------------------------- 15 | 16 | namespace Citrine.Core.Modules.Markov 17 | { 18 | using System; 19 | using System.Collections; 20 | using System.Collections.Generic; 21 | using System.Text.RegularExpressions; 22 | 23 | /// 24 | /// Defines the TinySegmenter type. 25 | /// Super compact Japanese tokenizer in Javascript. 26 | /// 27 | public class TinySegmenter 28 | { 29 | public static TinySegmenter Instance { get; } = new TinySegmenter(); 30 | 31 | /// 32 | /// Initializes a new instance of the TinySegmenter class. 33 | /// 34 | public TinySegmenter() 35 | { 36 | var patterns = new Dictionary 37 | { 38 | { "[一二三四五六七八九十百千万億兆]", "M" }, 39 | { "[一-龠々〆ヵヶ]", "H" }, 40 | { "[ぁ-ん]", "I" }, 41 | { "[ァ-ヴーア-ン゙ー]", "K" }, 42 | { "[a-zA-Za-zA-Z]", "A" }, 43 | { "[0-90-9]", "N" }, 44 | }; 45 | 46 | // Create regex for each pattern and add to chartypes 47 | this.chartype_ = new Dictionary(); 48 | foreach (var pattern in patterns) 49 | { 50 | var regexp = new Regex(pattern.Key); 51 | this.chartype_.Add(regexp, pattern.Value); 52 | } 53 | } 54 | 55 | /// 56 | /// Segment (tokenize) the given string of Japanese text 57 | /// 58 | /// The string to segment. 59 | /// A list of segments for the given string 60 | public List Segment(string input) 61 | { 62 | if (String.IsNullOrEmpty(input)) 63 | { 64 | return new List(); 65 | } 66 | 67 | var result = new List(); 68 | 69 | var seg = new List { "B3", "B2", "B1" }; 70 | var ctype = new List { "O", "O", "O" }; 71 | 72 | foreach (var character in input) 73 | { 74 | seg.Add(character.ToString()); 75 | ctype.Add(this.Ctype(character.ToString())); 76 | } 77 | 78 | seg.Add("E1"); 79 | seg.Add("E2"); 80 | seg.Add("E3"); 81 | ctype.Add("O"); 82 | ctype.Add("O"); 83 | ctype.Add("O"); 84 | var word = seg[3]; 85 | var p1 = "U"; 86 | var p2 = "U"; 87 | var p3 = "U"; 88 | for (var i = 4; i < seg.Count - 3; ++i) 89 | { 90 | var score = BIAS; 91 | var w1 = seg[i - 3]; 92 | var w2 = seg[i - 2]; 93 | var w3 = seg[i - 1]; 94 | var w4 = seg[i]; 95 | var w5 = seg[i + 1]; 96 | var w6 = seg[i + 2]; 97 | var c1 = ctype[i - 3]; 98 | var c2 = ctype[i - 2]; 99 | var c3 = ctype[i - 1]; 100 | var c4 = ctype[i]; 101 | var c5 = ctype[i + 1]; 102 | var c6 = ctype[i + 2]; 103 | score += DictionaryValueOrZero(UP1, p1); 104 | score += DictionaryValueOrZero(UP2, p2); 105 | score += DictionaryValueOrZero(UP3, p3); 106 | score += DictionaryValueOrZero(BP1, p1 + p2); 107 | score += DictionaryValueOrZero(BP2, p2 + p3); 108 | score += DictionaryValueOrZero(UW1, w1); 109 | score += DictionaryValueOrZero(UW2, w2); 110 | score += DictionaryValueOrZero(UW3, w3); 111 | score += DictionaryValueOrZero(UW4, w4); 112 | score += DictionaryValueOrZero(UW5, w5); 113 | score += DictionaryValueOrZero(UW6, w6); 114 | score += DictionaryValueOrZero(BW1, w2 + w3); 115 | score += DictionaryValueOrZero(BW2, w3 + w4); 116 | score += DictionaryValueOrZero(BW3, w4 + w5); 117 | score += DictionaryValueOrZero(TW1, w1 + w2 + w3); 118 | score += DictionaryValueOrZero(TW2, w2 + w3 + w4); 119 | score += DictionaryValueOrZero(TW3, w3 + w4 + w5); 120 | score += DictionaryValueOrZero(TW4, w4 + w5 + w6); 121 | score += DictionaryValueOrZero(UC1, c1); 122 | score += DictionaryValueOrZero(UC2, c2); 123 | score += DictionaryValueOrZero(UC3, c3); 124 | score += DictionaryValueOrZero(UC4, c4); 125 | score += DictionaryValueOrZero(UC5, c5); 126 | score += DictionaryValueOrZero(UC6, c6); 127 | score += DictionaryValueOrZero(BC1, c2 + c3); 128 | score += DictionaryValueOrZero(BC2, c3 + c4); 129 | score += DictionaryValueOrZero(BC3, c4 + c5); 130 | score += DictionaryValueOrZero(TC1, c1 + c2 + c3); 131 | score += DictionaryValueOrZero(TC2, c2 + c3 + c4); 132 | score += DictionaryValueOrZero(TC3, c3 + c4 + c5); 133 | score += DictionaryValueOrZero(TC4, c4 + c5 + c6); 134 | score += DictionaryValueOrZero(UQ1, p1 + c1); 135 | score += DictionaryValueOrZero(UQ2, p2 + c2); 136 | score += DictionaryValueOrZero(UQ1, p3 + c3); 137 | score += DictionaryValueOrZero(BQ1, p2 + c2 + c3); 138 | score += DictionaryValueOrZero(BQ2, p2 + c3 + c4); 139 | score += DictionaryValueOrZero(BQ3, p3 + c2 + c3); 140 | score += DictionaryValueOrZero(BQ4, p3 + c3 + c4); 141 | score += DictionaryValueOrZero(TQ1, p2 + c1 + c2 + c3); 142 | score += DictionaryValueOrZero(TQ2, p2 + c2 + c3 + c4); 143 | score += DictionaryValueOrZero(TQ3, p3 + c1 + c2 + c3); 144 | score += DictionaryValueOrZero(TQ4, p3 + c2 + c3 + c4); 145 | 146 | var p = "O"; 147 | if (score > 0) 148 | { 149 | result.Add(word); 150 | word = string.Empty; 151 | p = "B"; 152 | } 153 | 154 | p1 = p2; 155 | p2 = p3; 156 | p3 = p; 157 | word += seg[i]; 158 | } 159 | 160 | result.Add(word); 161 | 162 | return result; 163 | } 164 | 165 | /// 166 | /// Returns the value for the given hashtable key if present, or zero if not 167 | /// 168 | /// The hashtable to check for the key 169 | /// The key to check for existence 170 | /// The value for the given dictionary key if present, or zero if not 171 | private static int DictionaryValueOrZero(Hashtable hashtable, string key) 172 | { 173 | var value = hashtable[key]; 174 | 175 | return (int)(value ?? 0); 176 | } 177 | 178 | /// 179 | /// Find a matching character type for the given input string using regular expressions 180 | /// 181 | /// The input. 182 | /// Something else 183 | private string Ctype(string input) 184 | { 185 | foreach (var chartype in this.chartype_) 186 | { 187 | if (chartype.Key.IsMatch(input)) 188 | { 189 | return chartype.Value; 190 | } 191 | } 192 | 193 | return "O"; 194 | } 195 | 196 | private const int BIAS = -332; 197 | 198 | private readonly Dictionary chartype_; 199 | 200 | private readonly Hashtable BC1 = new Hashtable { { "HH", 6 }, { "II", 2461 }, { "KH", 406 }, { "OH", -1378 } }; 201 | private readonly Hashtable BC2 = new Hashtable { { "AA", -3267 }, { "AI", 2744 }, { "AN", -878 }, { "HH", -4070 }, { "HM", -1711 }, { "HN", 4012 }, { "HO", 3761 }, { "IA", 1327 }, { "IH", -1184 }, { "II", -1332 }, { "IK", 1721 }, { "IO", 5492 }, { "KI", 3831 }, { "KK", -8741 }, { "MH", -3132 }, { "MK", 3334 }, { "OO", -2920 } }; 202 | private readonly Hashtable BC3 = new Hashtable { { "HH", 996 }, { "HI", 626 }, { "HK", -721 }, { "HN", -1307 }, { "HO", -836 }, { "IH", -301 }, { "KK", 2762 }, { "MK", 1079 }, { "MM", 4034 }, { "OA", -1652 }, { "OH", 266 } }; 203 | private readonly Hashtable BP1 = new Hashtable { { "BB", 295 }, { "OB", 304 }, { "OO", -125 }, { "UB", 352 } }; 204 | private readonly Hashtable BP2 = new Hashtable { { "BO", 60 }, { "OO", -1762 } }; 205 | private readonly Hashtable BQ1 = new Hashtable { { "BHH", 1150 }, { "BHM", 1521 }, { "BII", -1158 }, { "BIM", 886 }, { "BMH", 1208 }, { "BNH", 449 }, { "BOH", -91 }, { "BOO", -2597 }, { "OHI", 451 }, { "OIH", -296 }, { "OKA", 1851 }, { "OKH", -1020 }, { "OKK", 904 }, { "OOO", 2965 } }; 206 | private readonly Hashtable BQ2 = new Hashtable { { "BHH", 118 }, { "BHI", -1159 }, { "BHM", 466 }, { "BIH", -919 }, { "BKK", -1720 }, { "BKO", 864 }, { "OHH", -1139 }, { "OHM", -181 }, { "OIH", 153 }, { "UHI", -1146 } }; 207 | private readonly Hashtable BQ3 = new Hashtable { { "BHH", -792 }, { "BHI", 2664 }, { "BII", -299 }, { "BKI", 419 }, { "BMH", 937 }, { "BMM", 8335 }, { "BNN", 998 }, { "BOH", 775 }, { "OHH", 2174 }, { "OHM", 439 }, { "OII", 280 }, { "OKH", 1798 }, { "OKI", -793 }, { "OKO", -2242 }, { "OMH", -2402 }, { "OOO", 11699 } }; 208 | private readonly Hashtable BQ4 = new Hashtable { { "BHH", -3895 }, { "BIH", 3761 }, { "BII", -4654 }, { "BIK", 1348 }, { "BKK", -1806 }, { "BMI", -3385 }, { "BOO", -12396 }, { "OAH", 926 }, { "OHH", 266 }, { "OHK", -2036 }, { "ONN", -973 } }; 209 | private readonly Hashtable BW1 = new Hashtable { { ",と", 660 }, { ",同", 727 }, { "B1あ", 1404 }, { "B1同", 542 }, { "、と", 660 }, { "、同", 727 }, { "」と", 1682 }, { "あっ", 1505 }, { "いう", 1743 }, { "いっ", -2055 }, { "いる", 672 }, { "うし", -4817 }, { "うん", 665 }, { "から", 3472 }, { "がら", 600 }, { "こう", -790 }, { "こと", 2083 }, { "こん", -1262 }, { "さら", -4143 }, { "さん", 4573 }, { "した", 2641 }, { "して", 1104 }, { "すで", -3399 }, { "そこ", 1977 }, { "それ", -871 }, { "たち", 1122 }, { "ため", 601 }, { "った", 3463 }, { "つい", -802 }, { "てい", 805 }, { "てき", 1249 }, { "でき", 1127 }, { "です", 3445 }, { "では", 844 }, { "とい", -4915 }, { "とみ", 1922 }, { "どこ", 3887 }, { "ない", 5713 }, { "なっ", 3015 }, { "など", 7379 }, { "なん", -1113 }, { "にし", 2468 }, { "には", 1498 }, { "にも", 1671 }, { "に対", -912 }, { "の一", -501 }, { "の中", 741 }, { "ませ", 2448 }, { "まで", 1711 }, { "まま", 2600 }, { "まる", -2155 }, { "やむ", -1947 }, { "よっ", -2565 }, { "れた", 2369 }, { "れで", -913 }, { "をし", 1860 }, { "を見", 731 }, { "亡く", -1886 }, { "京都", 2558 }, { "取り", -2784 }, { "大き", -2604 }, { "大阪", 1497 }, { "平方", -2314 }, { "引き", -1336 }, { "日本", -195 }, { "本当", -2423 }, { "毎日", -2113 }, { "目指", -724 }, { "B1あ", 1404 }, { "B1同", 542 }, { "」と", 1682 } }; 210 | private readonly Hashtable BW2 = new Hashtable { { "..", -11822 }, { "11", -669 }, { "――", -5730 }, { "−−", -13175 }, { "いう", -1609 }, { "うか", 2490 }, { "かし", -1350 }, { "かも", -602 }, { "から", -7194 }, { "かれ", 4612 }, { "がい", 853 }, { "がら", -3198 }, { "きた", 1941 }, { "くな", -1597 }, { "こと", -8392 }, { "この", -4193 }, { "させ", 4533 }, { "され", 13168 }, { "さん", -3977 }, { "しい", -1819 }, { "しか", -545 }, { "した", 5078 }, { "して", 972 }, { "しな", 939 }, { "その", -3744 }, { "たい", -1253 }, { "たた", -662 }, { "ただ", -3857 }, { "たち", -786 }, { "たと", 1224 }, { "たは", -939 }, { "った", 4589 }, { "って", 1647 }, { "っと", -2094 }, { "てい", 6144 }, { "てき", 3640 }, { "てく", 2551 }, { "ては", -3110 }, { "ても", -3065 }, { "でい", 2666 }, { "でき", -1528 }, { "でし", -3828 }, { "です", -4761 }, { "でも", -4203 }, { "とい", 1890 }, { "とこ", -1746 }, { "とと", -2279 }, { "との", 720 }, { "とみ", 5168 }, { "とも", -3941 }, { "ない", -2488 }, { "なが", -1313 }, { "など", -6509 }, { "なの", 2614 }, { "なん", 3099 }, { "にお", -1615 }, { "にし", 2748 }, { "にな", 2454 }, { "によ", -7236 }, { "に対", -14943 }, { "に従", -4688 }, { "に関", -11388 }, { "のか", 2093 }, { "ので", -7059 }, { "のに", -6041 }, { "のの", -6125 }, { "はい", 1073 }, { "はが", -1033 }, { "はず", -2532 }, { "ばれ", 1813 }, { "まし", -1316 }, { "まで", -6621 }, { "まれ", 5409 }, { "めて", -3153 }, { "もい", 2230 }, { "もの", -10713 }, { "らか", -944 }, { "らし", -1611 }, { "らに", -1897 }, { "りし", 651 }, { "りま", 1620 }, { "れた", 4270 }, { "れて", 849 }, { "れば", 4114 }, { "ろう", 6067 }, { "われ", 7901 }, { "を通", -11877 }, { "んだ", 728 }, { "んな", -4115 }, { "一人", 602 }, { "一方", -1375 }, { "一日", 970 }, { "一部", -1051 }, { "上が", -4479 }, { "会社", -1116 }, { "出て", 2163 }, { "分の", -7758 }, { "同党", 970 }, { "同日", -913 }, { "大阪", -2471 }, { "委員", -1250 }, { "少な", -1050 }, { "年度", -8669 }, { "年間", -1626 }, { "府県", -2363 }, { "手権", -1982 }, { "新聞", -4066 }, { "日新", -722 }, { "日本", -7068 }, { "日米", 3372 }, { "曜日", -601 }, { "朝鮮", -2355 }, { "本人", -2697 }, { "東京", -1543 }, { "然と", -1384 }, { "社会", -1276 }, { "立て", -990 }, { "第に", -1612 }, { "米国", -4268 }, { "11", -669 } }; 211 | private readonly Hashtable BW3 = new Hashtable { { "あた", -2194 }, { "あり", 719 }, { "ある", 3846 }, { "い.", -1185 }, { "い。", -1185 }, { "いい", 5308 }, { "いえ", 2079 }, { "いく", 3029 }, { "いた", 2056 }, { "いっ", 1883 }, { "いる", 5600 }, { "いわ", 1527 }, { "うち", 1117 }, { "うと", 4798 }, { "えと", 1454 }, { "か.", 2857 }, { "か。", 2857 }, { "かけ", -743 }, { "かっ", -4098 }, { "かに", -669 }, { "から", 6520 }, { "かり", -2670 }, { "が}, ", 1816 }, { "が、", 1816 }, { "がき", -4855 }, { "がけ", -1127 }, { "がっ", -913 }, { "がら", -4977 }, { "がり", -2064 }, { "きた", 1645 }, { "けど", 1374 }, { "こと", 7397 }, { "この", 1542 }, { "ころ", -2757 }, { "さい", -714 }, { "さを", 976 }, { "し}, ", 1557 }, { "し、", 1557 }, { "しい", -3714 }, { "した", 3562 }, { "して", 1449 }, { "しな", 2608 }, { "しま", 1200 }, { "す.", -1310 }, { "す。", -1310 }, { "する", 6521 }, { "ず}, ", 3426 }, { "ず、", 3426 }, { "ずに", 841 }, { "そう", 428 }, { "た.", 8875 }, { "た。", 8875 }, { "たい", -594 }, { "たの", 812 }, { "たり", -1183 }, { "たる", -853 }, { "だ.", 4098 }, { "だ。", 4098 }, { "だっ", 1004 }, { "った", -4748 }, { "って", 300 }, { "てい", 6240 }, { "てお", 855 }, { "ても", 302 }, { "です", 1437 }, { "でに", -1482 }, { "では", 2295 }, { "とう", -1387 }, { "とし", 2266 }, { "との", 541 }, { "とも", -3543 }, { "どう", 4664 }, { "ない", 1796 }, { "なく", -903 }, { "など", 2135 }, { "に}, ", -1021 }, { "に、", -1021 }, { "にし", 1771 }, { "にな", 1906 }, { "には", 2644 }, { "の}, ", -724 }, { "の、", -724 }, { "の子", -1000 }, { "は}, ", 1337 }, { "は、", 1337 }, { "べき", 2181 }, { "まし", 1113 }, { "ます", 6943 }, { "まっ", -1549 }, { "まで", 6154 }, { "まれ", -793 }, { "らし", 1479 }, { "られ", 6820 }, { "るる", 3818 }, { "れ}, ", 854 }, { "れ、", 854 }, { "れた", 1850 }, { "れて", 1375 }, { "れば", -3246 }, { "れる", 1091 }, { "われ", -605 }, { "んだ", 606 }, { "んで", 798 }, { "カ月", 990 }, { "会議", 860 }, { "入り", 1232 }, { "大会", 2217 }, { "始め", 1681 }, { "市", 965 }, { "新聞", -5055 }, { "日}, ", 974 }, { "日、", 974 }, { "社会", 2024 }, { "カ月", 990 } }; 212 | private readonly Hashtable TC1 = new Hashtable { { "AAA", 1093 }, { "HHH", 1029 }, { "HHM", 580 }, { "HII", 998 }, { "HOH", -390 }, { "HOM", -331 }, { "IHI", 1169 }, { "IOH", -142 }, { "IOI", -1015 }, { "IOM", 467 }, { "MMH", 187 }, { "OOI", -1832 } }; 213 | private readonly Hashtable TC2 = new Hashtable { { "HHO", 2088 }, { "HII", -1023 }, { "HMM", -1154 }, { "IHI", -1965 }, { "KKH", 703 }, { "OII", -2649 } }; 214 | private readonly Hashtable TC3 = new Hashtable { { "AAA", -294 }, { "HHH", 346 }, { "HHI", -341 }, { "HII", -1088 }, { "HIK", 731 }, { "HOH", -1486 }, { "IHH", 128 }, { "IHI", -3041 }, { "IHO", -1935 }, { "IIH", -825 }, { "IIM", -1035 }, { "IOI", -542 }, { "KHH", -1216 }, { "KKA", 491 }, { "KKH", -1217 }, { "KOK", -1009 }, { "MHH", -2694 }, { "MHM", -457 }, { "MHO", 123 }, { "MMH", -471 }, { "NNH", -1689 }, { "NNO", 662 }, { "OHO", -3393 } }; 215 | private readonly Hashtable TC4 = new Hashtable { { "HHH", -203 }, { "HHI", 1344 }, { "HHK", 365 }, { "HHM", -122 }, { "HHN", 182 }, { "HHO", 669 }, { "HIH", 804 }, { "HII", 679 }, { "HOH", 446 }, { "IHH", 695 }, { "IHO", -2324 }, { "IIH", 321 }, { "III", 1497 }, { "IIO", 656 }, { "IOO", 54 }, { "KAK", 4845 }, { "KKA", 3386 }, { "KKK", 3065 }, { "MHH", -405 }, { "MHI", 201 }, { "MMH", -241 }, { "MMM", 661 }, { "MOM", 841 } }; 216 | private readonly Hashtable TQ1 = new Hashtable { { "BHHH", -227 }, { "BHHI", 316 }, { "BHIH", -132 }, { "BIHH", 60 }, { "BIII", 1595 }, { "BNHH", -744 }, { "BOHH", 225 }, { "BOOO", -908 }, { "OAKK", 482 }, { "OHHH", 281 }, { "OHIH", 249 }, { "OIHI", 200 }, { "OIIH", -68 } }; 217 | private readonly Hashtable TQ2 = new Hashtable { { "BIHH", -1401 }, { "BIII", -1033 }, { "BKAK", -543 }, { "BOOO", -5591 } }; 218 | private readonly Hashtable TQ3 = new Hashtable { { "BHHH", 478 }, { "BHHM", -1073 }, { "BHIH", 222 }, { "BHII", -504 }, { "BIIH", -116 }, { "BIII", -105 }, { "BMHI", -863 }, { "BMHM", -464 }, { "BOMH", 620 }, { "OHHH", 346 }, { "OHHI", 1729 }, { "OHII", 997 }, { "OHMH", 481 }, { "OIHH", 623 }, { "OIIH", 1344 }, { "OKAK", 2792 }, { "OKHH", 587 }, { "OKKA", 679 }, { "OOHH", 110 }, { "OOII", -685 } }; 219 | private readonly Hashtable TQ4 = new Hashtable { { "BHHH", -721 }, { "BHHM", -3604 }, { "BHII", -966 }, { "BIIH", -607 }, { "BIII", -2181 }, { "OAAA", -2763 }, { "OAKK", 180 }, { "OHHH", -294 }, { "OHHI", 2446 }, { "OHHO", 480 }, { "OHIH", -1573 }, { "OIHH", 1935 }, { "OIHI", -493 }, { "OIIH", 626 }, { "OIII", -4007 }, { "OKAK", -8156 } }; 220 | private readonly Hashtable TW1 = new Hashtable { { "につい", -4681 }, { "東京都", 2026 } }; 221 | private readonly Hashtable TW2 = new Hashtable { { "ある程", -2049 }, { "いった", -1256 }, { "ころが", -2434 }, { "しょう", 3873 }, { "その後", -4430 }, { "だって", -1049 }, { "ていた", 1833 }, { "として", -4657 }, { "ともに", -4517 }, { "もので", 1882 }, { "一気に", -792 }, { "初めて", -1512 }, { "同時に", -8097 }, { "大きな", -1255 }, { "対して", -2721 }, { "社会党", -3216 } }; 222 | private readonly Hashtable TW3 = new Hashtable { { "いただ", -1734 }, { "してい", 1314 }, { "として", -4314 }, { "につい", -5483 }, { "にとっ", -5989 }, { "に当た", -6247 }, { "ので}, ", -727 }, { "ので、", -727 }, { "のもの", -600 }, { "れから", -3752 }, { "十二月", -2287 } }; 223 | private readonly Hashtable TW4 = new Hashtable { { "いう.", 8576 }, { "いう。", 8576 }, { "からな", -2348 }, { "してい", 2958 }, { "たが}, ", 1516 }, { "たが、", 1516 }, { "ている", 1538 }, { "という", 1349 }, { "ました", 5543 }, { "ません", 1097 }, { "ようと", -4258 }, { "よると", 5865 } }; 224 | private readonly Hashtable UC1 = new Hashtable { { "A", 484 }, { "K", 93 }, { "M", 645 }, { "O", -505 } }; 225 | private readonly Hashtable UC2 = new Hashtable { { "A", 819 }, { "H", 1059 }, { "I", 409 }, { "M", 3987 }, { "N", 5775 }, { "O", 646 } }; 226 | private readonly Hashtable UC3 = new Hashtable { { "A", -1370 }, { "I", 2311 } }; 227 | private readonly Hashtable UC4 = new Hashtable { { "A", -2643 }, { "H", 1809 }, { "I", -1032 }, { "K", -3450 }, { "M", 3565 }, { "N", 3876 }, { "O", 6646 } }; 228 | private readonly Hashtable UC5 = new Hashtable { { "H", 313 }, { "I", -1238 }, { "K", -799 }, { "M", 539 }, { "O", -831 } }; 229 | private readonly Hashtable UC6 = new Hashtable { { "H", -506 }, { "I", -253 }, { "K", 87 }, { "M", 247 }, { "O", -387 } }; 230 | private readonly Hashtable UP1 = new Hashtable { { "O", -214 } }; 231 | private readonly Hashtable UP2 = new Hashtable { { "B", 69 }, { "O", 935 } }; 232 | private readonly Hashtable UP3 = new Hashtable { { "B", 189 } }; 233 | private readonly Hashtable UQ1 = new Hashtable { { "BH", 21 }, { "BI", -12 }, { "BK", -99 }, { "BN", 142 }, { "BO", -56 }, { "OH", -95 }, { "OI", 477 }, { "OK", 410 }, { "OO", -2422 } }; 234 | private readonly Hashtable UQ2 = new Hashtable { { "BH", 216 }, { "BI", 113 }, { "OK", 1759 } }; 235 | private readonly Hashtable UW1 = new Hashtable { { "}, ", 156 }, { "、", 156 }, { "「", -463 }, { "あ", -941 }, { "う", -127 }, { "が", -553 }, { "き", 121 }, { "こ", 505 }, { "で", -201 }, { "と", -547 }, { "ど", -123 }, { "に", -789 }, { "の", -185 }, { "は", -847 }, { "も", -466 }, { "や", -470 }, { "よ", 182 }, { "ら", -292 }, { "り", 208 }, { "れ", 169 }, { "を", -446 }, { "ん", -137 }, { "・", -135 }, { "主", -402 }, { "京", -268 }, { "区", -912 }, { "午", 871 }, { "国", -460 }, { "大", 561 }, { "委", 729 }, { "市", -411 }, { "日", -141 }, { "理", 361 }, { "生", -408 }, { "県", -386 }, { "都", -718 }, { "「", -463 }, { "・", -135 } }; 236 | private readonly Hashtable UW2 = new Hashtable { { "}, ", -829 }, { "、", -829 }, { "〇", 892 }, { "「", -645 }, { "」", 3145 }, { "あ", -538 }, { "い", 505 }, { "う", 134 }, { "お", -502 }, { "か", 1454 }, { "が", -856 }, { "く", -412 }, { "こ", 1141 }, { "さ", 878 }, { "ざ", 540 }, { "し", 1529 }, { "す", -675 }, { "せ", 300 }, { "そ", -1011 }, { "た", 188 }, { "だ", 1837 }, { "つ", -949 }, { "て", -291 }, { "で", -268 }, { "と", -981 }, { "ど", 1273 }, { "な", 1063 }, { "に", -1764 }, { "の", 130 }, { "は", -409 }, { "ひ", -1273 }, { "べ", 1261 }, { "ま", 600 }, { "も", -1263 }, { "や", -402 }, { "よ", 1639 }, { "り", -579 }, { "る", -694 }, { "れ", 571 }, { "を", -2516 }, { "ん", 2095 }, { "ア", -587 }, { "カ", 306 }, { "キ", 568 }, { "ッ", 831 }, { "三", -758 }, { "不", -2150 }, { "世", -302 }, { "中", -968 }, { "主", -861 }, { "事", 492 }, { "人", -123 }, { "会", 978 }, { "保", 362 }, { "入", 548 }, { "初", -3025 }, { "副", -1566 }, { "北", -3414 }, { "区", -422 }, { "大", -1769 }, { "天", -865 }, { "太", -483 }, { "子", -1519 }, { "学", 760 }, { "実", 1023 }, { "小", -2009 }, { "市", -813 }, { "年", -1060 }, { "強", 1067 }, { "手", -1519 }, { "揺", -1033 }, { "政", 1522 }, { "文", -1355 }, { "新", -1682 }, { "日", -1815 }, { "明", -1462 }, { "最", -630 }, { "朝", -1843 }, { "本", -1650 }, { "東", -931 }, { "果", -665 }, { "次", -2378 }, { "民", -180 }, { "気", -1740 }, { "理", 752 }, { "発", 529 }, { "目", -1584 }, { "相", -242 }, { "県", -1165 }, { "立", -763 }, { "第", 810 }, { "米", 509 }, { "自", -1353 }, { "行", 838 }, { "西", -744 }, { "見", -3874 }, { "調", 1010 }, { "議", 1198 }, { "込", 3041 }, { "開", 1758 }, { "間", -1257 }, { "「", -645 }, { "」", 3145 }, { "ッ", 831 }, { "ア", -587 }, { "カ", 306 }, { "キ", 568 } }; 237 | private readonly Hashtable UW3 = new Hashtable { { "}, ", 4889 }, { "1", -800 }, { "−", -1723 }, { "、", 4889 }, { "々", -2311 }, { "〇", 5827 }, { "」", 2670 }, { "〓", -3573 }, { "あ", -2696 }, { "い", 1006 }, { "う", 2342 }, { "え", 1983 }, { "お", -4864 }, { "か", -1163 }, { "が", 3271 }, { "く", 1004 }, { "け", 388 }, { "げ", 401 }, { "こ", -3552 }, { "ご", -3116 }, { "さ", -1058 }, { "し", -395 }, { "す", 584 }, { "せ", 3685 }, { "そ", -5228 }, { "た", 842 }, { "ち", -521 }, { "っ", -1444 }, { "つ", -1081 }, { "て", 6167 }, { "で", 2318 }, { "と", 1691 }, { "ど", -899 }, { "な", -2788 }, { "に", 2745 }, { "の", 4056 }, { "は", 4555 }, { "ひ", -2171 }, { "ふ", -1798 }, { "へ", 1199 }, { "ほ", -5516 }, { "ま", -4384 }, { "み", -120 }, { "め", 1205 }, { "も", 2323 }, { "や", -788 }, { "よ", -202 }, { "ら", 727 }, { "り", 649 }, { "る", 5905 }, { "れ", 2773 }, { "わ", -1207 }, { "を", 6620 }, { "ん", -518 }, { "ア", 551 }, { "グ", 1319 }, { "ス", 874 }, { "ッ", -1350 }, { "ト", 521 }, { "ム", 1109 }, { "ル", 1591 }, { "ロ", 2201 }, { "ン", 278 }, { "・", -3794 }, { "一", -1619 }, { "下", -1759 }, { "世", -2087 }, { "両", 3815 }, { "中", 653 }, { "主", -758 }, { "予", -1193 }, { "二", 974 }, { "人", 2742 }, { "今", 792 }, { "他", 1889 }, { "以", -1368 }, { "低", 811 }, { "何", 4265 }, { "作", -361 }, { "保", -2439 }, { "元", 4858 }, { "党", 3593 }, { "全", 1574 }, { "公", -3030 }, { "六", 755 }, { "共", -1880 }, { "円", 5807 }, { "再", 3095 }, { "分", 457 }, { "初", 2475 }, { "別", 1129 }, { "前", 2286 }, { "副", 4437 }, { "力", 365 }, { "動", -949 }, { "務", -1872 }, { "化", 1327 }, { "北", -1038 }, { "区", 4646 }, { "千", -2309 }, { "午", -783 }, { "協", -1006 }, { "口", 483 }, { "右", 1233 }, { "各", 3588 }, { "合", -241 }, { "同", 3906 }, { "和", -837 }, { "員", 4513 }, { "国", 642 }, { "型", 1389 }, { "場", 1219 }, { "外", -241 }, { "妻", 2016 }, { "学", -1356 }, { "安", -423 }, { "実", -1008 }, { "家", 1078 }, { "小", -513 }, { "少", -3102 }, { "州", 1155 }, { "市", 3197 }, { "平", -1804 }, { "年", 2416 }, { "広", -1030 }, { "府", 1605 }, { "度", 1452 }, { "建", -2352 }, { "当", -3885 }, { "得", 1905 }, { "思", -1291 }, { "性", 1822 }, { "戸", -488 }, { "指", -3973 }, { "政", -2013 }, { "教", -1479 }, { "数", 3222 }, { "文", -1489 }, { "新", 1764 }, { "日", 2099 }, { "旧", 5792 }, { "昨", -661 }, { "時", -1248 }, { "曜", -951 }, { "最", -937 }, { "月", 4125 }, { "期", 360 }, { "李", 3094 }, { "村", 364 }, { "東", -805 }, { "核", 5156 }, { "森", 2438 }, { "業", 484 }, { "氏", 2613 }, { "民", -1694 }, { "決", -1073 }, { "法", 1868 }, { "海", -495 }, { "無", 979 }, { "物", 461 }, { "特", -3850 }, { "生", -273 }, { "用", 914 }, { "町", 1215 }, { "的", 7313 }, { "直", -1835 }, { "省", 792 }, { "県", 6293 }, { "知", -1528 }, { "私", 4231 }, { "税", 401 }, { "立", -960 }, { "第", 1201 }, { "米", 7767 }, { "系", 3066 }, { "約", 3663 }, { "級", 1384 }, { "統", -4229 }, { "総", 1163 }, { "線", 1255 }, { "者", 6457 }, { "能", 725 }, { "自", -2869 }, { "英", 785 }, { "見", 1044 }, { "調", -562 }, { "財", -733 }, { "費", 1777 }, { "車", 1835 }, { "軍", 1375 }, { "込", -1504 }, { "通", -1136 }, { "選", -681 }, { "郎", 1026 }, { "郡", 4404 }, { "部", 1200 }, { "金", 2163 }, { "長", 421 }, { "開", -1432 }, { "間", 1302 }, { "関", -1282 }, { "雨", 2009 }, { "電", -1045 }, { "非", 2066 }, { "駅", 1620 }, { "1", -800 }, { "」", 2670 }, { "・", -3794 }, { "ッ", -1350 }, { "ア", 551 }, { "グ", 1319 }, { "ス", 874 }, { "ト", 521 }, { "ム", 1109 }, { "ル", 1591 }, { "ロ", 2201 }, { "ン", 278 } }; 238 | private readonly Hashtable UW4 = new Hashtable { { "}, ", 3930 }, { ".", 3508 }, { "―", -4841 }, { "、", 3930 }, { "。", 3508 }, { "〇", 4999 }, { "「", 1895 }, { "」", 3798 }, { "〓", -5156 }, { "あ", 4752 }, { "い", -3435 }, { "う", -640 }, { "え", -2514 }, { "お", 2405 }, { "か", 530 }, { "が", 6006 }, { "き", -4482 }, { "ぎ", -3821 }, { "く", -3788 }, { "け", -4376 }, { "げ", -4734 }, { "こ", 2255 }, { "ご", 1979 }, { "さ", 2864 }, { "し", -843 }, { "じ", -2506 }, { "す", -731 }, { "ず", 1251 }, { "せ", 181 }, { "そ", 4091 }, { "た", 5034 }, { "だ", 5408 }, { "ち", -3654 }, { "っ", -5882 }, { "つ", -1659 }, { "て", 3994 }, { "で", 7410 }, { "と", 4547 }, { "な", 5433 }, { "に", 6499 }, { "ぬ", 1853 }, { "ね", 1413 }, { "の", 7396 }, { "は", 8578 }, { "ば", 1940 }, { "ひ", 4249 }, { "び", -4134 }, { "ふ", 1345 }, { "へ", 6665 }, { "べ", -744 }, { "ほ", 1464 }, { "ま", 1051 }, { "み", -2082 }, { "む", -882 }, { "め", -5046 }, { "も", 4169 }, { "ゃ", -2666 }, { "や", 2795 }, { "ょ", -1544 }, { "よ", 3351 }, { "ら", -2922 }, { "り", -9726 }, { "る", -14896 }, { "れ", -2613 }, { "ろ", -4570 }, { "わ", -1783 }, { "を", 13150 }, { "ん", -2352 }, { "カ", 2145 }, { "コ", 1789 }, { "セ", 1287 }, { "ッ", -724 }, { "ト", -403 }, { "メ", -1635 }, { "ラ", -881 }, { "リ", -541 }, { "ル", -856 }, { "ン", -3637 }, { "・", -4371 }, { "ー", -11870 }, { "一", -2069 }, { "中", 2210 }, { "予", 782 }, { "事", -190 }, { "井", -1768 }, { "人", 1036 }, { "以", 544 }, { "会", 950 }, { "体", -1286 }, { "作", 530 }, { "側", 4292 }, { "先", 601 }, { "党", -2006 }, { "共", -1212 }, { "内", 584 }, { "円", 788 }, { "初", 1347 }, { "前", 1623 }, { "副", 3879 }, { "力", -302 }, { "動", -740 }, { "務", -2715 }, { "化", 776 }, { "区", 4517 }, { "協", 1013 }, { "参", 1555 }, { "合", -1834 }, { "和", -681 }, { "員", -910 }, { "器", -851 }, { "回", 1500 }, { "国", -619 }, { "園", -1200 }, { "地", 866 }, { "場", -1410 }, { "塁", -2094 }, { "士", -1413 }, { "多", 1067 }, { "大", 571 }, { "子", -4802 }, { "学", -1397 }, { "定", -1057 }, { "寺", -809 }, { "小", 1910 }, { "屋", -1328 }, { "山", -1500 }, { "島", -2056 }, { "川", -2667 }, { "市", 2771 }, { "年", 374 }, { "庁", -4556 }, { "後", 456 }, { "性", 553 }, { "感", 916 }, { "所", -1566 }, { "支", 856 }, { "改", 787 }, { "政", 2182 }, { "教", 704 }, { "文", 522 }, { "方", -856 }, { "日", 1798 }, { "時", 1829 }, { "最", 845 }, { "月", -9066 }, { "木", -485 }, { "来", -442 }, { "校", -360 }, { "業", -1043 }, { "氏", 5388 }, { "民", -2716 }, { "気", -910 }, { "沢", -939 }, { "済", -543 }, { "物", -735 }, { "率", 672 }, { "球", -1267 }, { "生", -1286 }, { "産", -1101 }, { "田", -2900 }, { "町", 1826 }, { "的", 2586 }, { "目", 922 }, { "省", -3485 }, { "県", 2997 }, { "空", -867 }, { "立", -2112 }, { "第", 788 }, { "米", 2937 }, { "系", 786 }, { "約", 2171 }, { "経", 1146 }, { "統", -1169 }, { "総", 940 }, { "線", -994 }, { "署", 749 }, { "者", 2145 }, { "能", -730 }, { "般", -852 }, { "行", -792 }, { "規", 792 }, { "警", -1184 }, { "議", -244 }, { "谷", -1000 }, { "賞", 730 }, { "車", -1481 }, { "軍", 1158 }, { "輪", -1433 }, { "込", -3370 }, { "近", 929 }, { "道", -1291 }, { "選", 2596 }, { "郎", -4866 }, { "都", 1192 }, { "野", -1100 }, { "銀", -2213 }, { "長", 357 }, { "間", -2344 }, { "院", -2297 }, { "際", -2604 }, { "電", -878 }, { "領", -1659 }, { "題", -792 }, { "館", -1984 }, { "首", 1749 }, { "高", 2120 }, { "「", 1895 }, { "」", 3798 }, { "・", -4371 }, { "ッ", -724 }, { "ー", -11870 }, { "カ", 2145 }, { "コ", 1789 }, { "セ", 1287 }, { "ト", -403 }, { "メ", -1635 }, { "ラ", -881 }, { "リ", -541 }, { "ル", -856 }, { "ン", -3637 } }; 239 | private readonly Hashtable UW5 = new Hashtable { { "}, ", 465 }, { ".", -299 }, { "1", -514 }, { "E2", -32768 }, { "]", -2762 }, { "、", 465 }, { "。", -299 }, { "「", 363 }, { "あ", 1655 }, { "い", 331 }, { "う", -503 }, { "え", 1199 }, { "お", 527 }, { "か", 647 }, { "が", -421 }, { "き", 1624 }, { "ぎ", 1971 }, { "く", 312 }, { "げ", -983 }, { "さ", -1537 }, { "し", -1371 }, { "す", -852 }, { "だ", -1186 }, { "ち", 1093 }, { "っ", 52 }, { "つ", 921 }, { "て", -18 }, { "で", -850 }, { "と", -127 }, { "ど", 1682 }, { "な", -787 }, { "に", -1224 }, { "の", -635 }, { "は", -578 }, { "べ", 1001 }, { "み", 502 }, { "め", 865 }, { "ゃ", 3350 }, { "ょ", 854 }, { "り", -208 }, { "る", 429 }, { "れ", 504 }, { "わ", 419 }, { "を", -1264 }, { "ん", 327 }, { "イ", 241 }, { "ル", 451 }, { "ン", -343 }, { "中", -871 }, { "京", 722 }, { "会", -1153 }, { "党", -654 }, { "務", 3519 }, { "区", -901 }, { "告", 848 }, { "員", 2104 }, { "大", -1296 }, { "学", -548 }, { "定", 1785 }, { "嵐", -1304 }, { "市", -2991 }, { "席", 921 }, { "年", 1763 }, { "思", 872 }, { "所", -814 }, { "挙", 1618 }, { "新", -1682 }, { "日", 218 }, { "月", -4353 }, { "査", 932 }, { "格", 1356 }, { "機", -1508 }, { "氏", -1347 }, { "田", 240 }, { "町", -3912 }, { "的", -3149 }, { "相", 1319 }, { "省", -1052 }, { "県", -4003 }, { "研", -997 }, { "社", -278 }, { "空", -813 }, { "統", 1955 }, { "者", -2233 }, { "表", 663 }, { "語", -1073 }, { "議", 1219 }, { "選", -1018 }, { "郎", -368 }, { "長", 786 }, { "間", 1191 }, { "題", 2368 }, { "館", -689 }, { "1", -514 }, { "E2", -32768 }, { "「", 363 }, { "イ", 241 }, { "ル", 451 }, { "ン", -343 } }; 240 | private readonly Hashtable UW6 = new Hashtable { { "}, ", 227 }, { ".", 808 }, { "1", -270 }, { "E1", 306 }, { "、", 227 }, { "。", 808 }, { "あ", -307 }, { "う", 189 }, { "か", 241 }, { "が", -73 }, { "く", -121 }, { "こ", -200 }, { "じ", 1782 }, { "す", 383 }, { "た", -428 }, { "っ", 573 }, { "て", -1014 }, { "で", 101 }, { "と", -105 }, { "な", -253 }, { "に", -149 }, { "の", -417 }, { "は", -236 }, { "も", -206 }, { "り", 187 }, { "る", -135 }, { "を", 195 }, { "ル", -673 }, { "ン", -496 }, { "一", -277 }, { "中", 201 }, { "件", -800 }, { "会", 624 }, { "前", 302 }, { "区", 1792 }, { "員", -1212 }, { "委", 798 }, { "学", -960 }, { "市", 887 }, { "広", -695 }, { "後", 535 }, { "業", -697 }, { "相", 753 }, { "社", -507 }, { "福", 974 }, { "空", -822 }, { "者", 1811 }, { "連", 463 }, { "郎", 1082 }, { "1", -270 }, { "E1", 306 }, { "ル", -673 }, { "ン", -496 } }; 241 | } 242 | } --------------------------------------------------------------------------------