/// <summary> /// 文法を学習します。 /// </summary> private async Task LearnAsync(string text, UserStorage.UserRecord storage, DateTimeOffset now, string apiKey) { var result = await AnalysisAsync(apiKey, text); storage.Set("freetalk.lastLearnedAt", now); var nouns = storage.Get("freetalk.nouns", new List <string>()).Where(t => !t.IsMatch(@"^[a-z\-_0-9]+$")).ToList(); var verbs = storage.Get("freetalk.verbs", new List <string>()).ToList(); var adjectives = storage.Get("freetalk.adjectives", new List <string>()).ToList(); string?prefix = null; string?noun = null; for (var i = 0; i < result.Length; i++) { // ルール1: 名詞が連続する場合、その連続する名詞は1語として記憶する // ルール2: 接頭辞がある場合、登録時にくっつけて登録する // ルール3: next が接尾辞を指す場合、登録時にくっつけて登録する // ルール4: 名詞+助動詞(する)を検出した場合、動詞として登録する var(surface, reading, pos, baseform, group1, group2) = result[i]; var next = i < result.Length - 1 ? result[i + 1] : default; void RegisterNoun() { if (noun != null) { noun += pos == "接尾辞" ? baseform : null; if (!noun.IsMatch(@"^[a-z\-_0-9]$")) { nouns.Add(noun); lastLearnedWord = noun; } prefix = null; noun = null; } } switch (pos) { case "形容詞" when baseform != null: RegisterNoun(); adjectives.Add(baseform); lastLearnedWord = baseform; break; case "接頭辞": prefix = (prefix ?? "") + baseform; break; case "名詞": noun += baseform; break; case "助動詞" when baseform == "する": noun += baseform; if (!noun.IsMatch(@"^[a-z\-_0-9]+$")) { verbs.Add("サ変する," + noun); lastLearnedWord = noun; } prefix = null; noun = null; break; case "動詞": { RegisterNoun(); var verb = prefix + baseform; lastLearnedWord = verb; verbs.Add(group1 + "," + verb); prefix = null; noun = null; break; } default: RegisterNoun(); break; } } if (noun != null) { if (!noun.IsMatch(@"^[a-z\-_0-9]+$")) { nouns.Add(noun); lastLearnedWord = noun; } } const int limit = 200; storage.Set("freetalk.nouns", nouns.Distinct().TakeLast(limit).ToList()); storage.Set("freetalk.verbs", verbs.Distinct().TakeLast(limit).ToList()); storage.Set("freetalk.adjectives", adjectives.Distinct().TakeLast(limit).ToList()); }
/// <summary> /// トークを生成し投稿 /// </summary> private async Task Talk(UserStorage.UserRecord storage) { if (core == null) { return; } if (shell == null) { return; } var recent = storage.Get("freetalk.recent", new List <string>()); var pollRatio = storage.Get("freetalk.config.pollRatio", 30); var now = DateTime.Now; var today = now.Date; var hour = now.Hour; var lastBreakfastAt = storage.Get("freetalk.lastBreakfastAt", DateTime.MinValue.Date); var lastLunchAt = storage.Get("freetalk.lastLunchAt", DateTime.MinValue.Date); var lastSnackTimeAt = storage.Get("freetalk.lastSnackTimeAt", DateTime.MinValue.Date); var lastDinnerAt = storage.Get("freetalk.lastDinnerAt", DateTime.MinValue.Date); // ごはん投票は、毎tickごと抽選する var win = core.Random.Next(100) < pollRatio; if (win && lastBreakfastAt != today && hour >= 7 && hour <= 10) { await shell.PostAsync("朝ごはんどうしよ", choices : GenerateChoices(storage)); storage.Set("freetalk.lastBreakfastAt", now.Date); } else if (win && lastLunchAt != today && hour >= 11 && hour <= 13) { await shell.PostAsync("昼ごはんが決まらないので投票", choices : GenerateChoices(storage)); storage.Set("freetalk.lastLunchAt", now.Date); } else if (win && lastSnackTimeAt != today && hour == 15) { await shell.PostAsync("おやつの時間〜.何食べよう", choices : GenerateChoices(storage)); storage.Set("freetalk.lastSnackTimeAt", now.Date); } else if (win && lastDinnerAt != today && hour >= 17 && hour <= 19) { await shell.PostAsync("夜,何食べようかな", choices : GenerateChoices(storage)); storage.Set("freetalk.lastDinnerAt", now.Date); } else if (lastLearnedWord != null && core.Random.Next(100) < 10) { await shell.PostAsync(Topics.Learned.Random().Replace("$word$", lastLearnedWord)); } else { // ダブリが頻発しないように、直近の抽選履歴を見てかぶらないトピックを抽選する string s; var count = 0; var topics = ExtractTopics().ToArray(); do { s = topics.Random(); count++; } while (recent.Contains(s) && count < 1000); // 1000回超えても被ってしまうのなら諦めて最後に試行したものを採用 await shell.PostAsync(MapVariables(s)); // if (!s.Contains("$")) recent.Add(s); storage.Set("freetalk.recent", recent.TakeLast(topics.Length).ToList()); } lastLearnedWord = null; }