2024-01-22 17:35:01 +01:00
using System.Collections ;
2024-01-31 03:24:59 +01:00
using Newtonsoft.Json ;
2024-01-22 17:35:01 +01:00
using SoUnBot.AccessControl ;
2024-01-19 19:06:53 +01:00
using SoUnBot.ModuleLoader ;
2024-01-19 03:29:39 +01:00
using Telegram.Bot ;
using Telegram.Bot.Types ;
using Telegram.Bot.Types.Enums ;
2024-01-31 12:49:28 +01:00
using Telegram.Bot.Types.InputFiles ;
2024-01-19 03:29:39 +01:00
using Telegram.Bot.Types.ReplyMarkups ;
2024-01-31 12:49:28 +01:00
using File = System . IO . File ;
2024-01-19 03:29:39 +01:00
2024-01-19 19:06:53 +01:00
namespace SoUnBot.Modules.OttoLinux
2024-01-19 03:29:39 +01:00
{
public class BotGame : IModule
{
2024-01-19 19:06:53 +01:00
private AccessManager _accessManager ;
2024-01-19 03:29:39 +01:00
private List < Question > _questions ;
private string _questionsPath ;
private string _name ;
private bool _lock ;
2024-01-31 12:49:28 +01:00
private string _imgBaseDir ;
2024-01-19 03:29:39 +01:00
private Dictionary < long , OttoScore > _scores ;
private Dictionary < long , Question > _playingQuestions ;
private Dictionary < long , List < int > > _playedQuestions ;
private Dictionary < Question , OttoScore > _questionStats ;
private static Random _rng = new Random ( ) ;
2024-01-31 12:49:28 +01:00
public BotGame ( AccessManager accessManager , string name , string path , bool locke , string imgBaseDir , int version = 1 )
2024-01-19 03:29:39 +01:00
{
2024-01-19 19:06:53 +01:00
_accessManager = accessManager ;
2024-01-19 03:29:39 +01:00
_questionsPath = path ;
_name = name ;
_lock = locke ;
2024-01-31 12:49:28 +01:00
_imgBaseDir = imgBaseDir ;
2024-01-19 03:29:39 +01:00
_questions = new List < Question > ( ) ;
_scores = new Dictionary < long , OttoScore > ( ) ;
_playingQuestions = new Dictionary < long , Question > ( ) ;
_questionStats = new Dictionary < Question , OttoScore > ( ) ;
_playedQuestions = new Dictionary < long , List < int > > ( ) ;
if ( version = = 2 ) LoadQuestionsV2 ( ) ;
2024-01-31 03:24:59 +01:00
else if ( version = = 3 ) LoadQuestionsJSON ( ) ;
2024-01-19 03:29:39 +01:00
else LoadQuestions ( ) ;
}
2024-01-19 19:06:53 +01:00
public BotGame ( AccessManager accessManager )
2024-01-19 03:29:39 +01:00
{
2024-01-19 19:06:53 +01:00
_accessManager = accessManager ;
2024-01-19 03:29:39 +01:00
_questions = new List < Question > ( ) ;
_scores = new Dictionary < long , OttoScore > ( ) ;
_playingQuestions = new Dictionary < long , Question > ( ) ;
_questionStats = new Dictionary < Question , OttoScore > ( ) ;
_playedQuestions = new Dictionary < long , List < int > > ( ) ;
LoadQuestions ( ) ;
}
private void LoadQuestions ( )
{
var lines = System . IO . File . ReadAllLines ( _questionsPath ) ;
Question cur = null ;
var preEmpty = true ;
foreach ( var line in lines )
{
if ( line . Equals ( "" ) ) preEmpty = true ;
else preEmpty = false ;
if ( line . StartsWith ( ">" ) ) cur . AddAnswer ( line . Substring ( 2 ) , false ) ;
else if ( line . StartsWith ( "v" ) ) cur . AddAnswer ( line . Substring ( 2 ) , true ) ;
else
{
if ( ! preEmpty & & cur ! = null )
{
cur . Append ( "\n" + line ) ;
continue ;
}
if ( cur ! = null ) _questions . Add ( cur ) ;
cur = new Question ( line ) ;
}
}
_questions . Add ( cur ) ;
2024-01-22 17:35:01 +01:00
SanitizeQuestions ( ) ;
2024-01-19 03:29:39 +01:00
}
private void LoadQuestionsV2 ( )
{
var questions = System . IO . Directory . GetFileSystemEntries ( _questionsPath ) ;
foreach ( var questPath in questions )
{
var quest = System . IO . File . ReadAllText ( questPath + "/quest.txt" ) ;
if ( quest . StartsWith ( "img=" ) ) quest = quest . Insert ( quest . IndexOf ( "\n" ) + 1 , questPath . Split ( new char [ ] { '/' , '\\' } ) . Last ( ) + ". " ) ;
else quest = questPath . Split ( new char [ ] { '/' , '\\' } ) . Last ( ) + ". " + quest ;
Question cur = new Question ( quest ) ;
var shuffledAnsPaths = Directory . GetFiles ( questPath ) . OrderBy ( a = > _rng . Next ( ) ) . ToArray ( ) ;
foreach ( var ansPath in shuffledAnsPaths )
{
if ( ansPath . EndsWith ( "quest.txt" ) ) continue ;
if ( ansPath . EndsWith ( ".png" ) ) continue ;
var ans = System . IO . File . ReadAllText ( ansPath ) ;
if ( ansPath . EndsWith ( "correct.txt" ) ) cur . AddAnswer ( ans , true ) ;
else cur . AddAnswer ( ans , false ) ;
}
_questions . Add ( cur ) ;
}
2024-01-22 17:35:01 +01:00
SanitizeQuestions ( ) ;
}
2024-01-31 03:24:59 +01:00
private void LoadQuestionsJSON ( )
{
var json = System . IO . File . ReadAllText ( _questionsPath ) ;
var quests = JsonConvert . DeserializeObject < Question [ ] > ( json ) ;
if ( quests ! = null ) _questions = quests . ToList ( ) ;
}
2024-01-22 17:35:01 +01:00
private void SanitizeQuestions ( )
{
var invalidQuestions = new List < Question > ( ) ;
foreach ( var qst in _questions )
{
while ( qst . Quest . StartsWith ( "\n" ) ) qst . Quest = qst . Quest . Substring ( 1 ) ;
for ( int i = 0 ; i < qst . Answers . Count ; i + + )
{
while ( qst . Answers [ i ] . StartsWith ( "\n" ) ) qst . Answers [ i ] = qst . Answers [ i ] . Substring ( 1 ) ;
}
if ( qst . Quest = = "" )
{
invalidQuestions . Add ( qst ) ;
Console . WriteLine ( "an empty question was found, skipping it" ) ;
}
else if ( qst . Answers . Count = = 0 )
{
invalidQuestions . Add ( qst ) ;
Console . WriteLine ( $"The following question: {qst.Quest} \nhas no answers, skipping it" ) ;
}
}
_questions = _questions . Except ( invalidQuestions ) . ToList ( ) ;
2024-01-19 03:29:39 +01:00
}
public Question PickRandomQuestion ( long player , ITelegramBotClient botClient )
{
//WebRequest w = WebRequest.Create($"https://www.random.org/integers/?num=1&min=1&max={_questions.Count - 1 }&col=1&base=10&format=plain&rnd=new");
//w.Method = "GET";
//var number = int.Parse(new StreamReader(w.GetResponse().GetResponseStream()).ReadToEnd());
2024-01-22 17:35:01 +01:00
var number = _rng . Next ( 0 , _questions . Count - 1 ) ;
while ( _questions [ number ] . Quest = = "" )
{
number = _rng . Next ( 0 , _questions . Count - 1 ) ;
}
2024-01-19 03:29:39 +01:00
if ( ! _playedQuestions . ContainsKey ( player ) ) _playedQuestions . Add ( player , new List < int > ( ) ) ;
if ( _playedQuestions [ player ] . Count > = _questions . Count )
{
_playedQuestions [ player ] . Clear ( ) ;
botClient . SendTextMessageAsync (
chatId : player ,
text : $"🥳🥳🥳 Congratulazioni! Hai risposto a tutte le {_questions.Count} domande!"
) ;
}
while ( _playedQuestions [ player ] . Contains ( number ) )
{
if ( number < _questions . Count - 1 ) number + + ;
else number = 0 ;
}
_playedQuestions [ player ] . Add ( number ) ;
return _questions [ number ] ;
}
public string Cmd ( )
{
return GetName ( ) ;
}
async void IModule . ProcessUpdate ( ITelegramBotClient botClient , Update update , CancellationToken cancellationToken )
{
var uid = update . Message . From . Id ;
if ( _lock )
{
2024-01-19 19:06:53 +01:00
if ( ! _accessManager . CheckPermission ( update . Message . From , Cmd ( ) , botClient ) ) return ;
2024-01-19 03:29:39 +01:00
}
else
{
2024-01-19 19:06:53 +01:00
if ( ! _accessManager . CheckPermission ( uid , Cmd ( ) ) )
2024-01-19 03:29:39 +01:00
{
2024-01-19 19:06:53 +01:00
_accessManager . GrantPermission ( uid , Cmd ( ) ) ;
2024-01-19 03:29:39 +01:00
await botClient . SendTextMessageAsync (
2024-01-19 19:06:53 +01:00
chatId : _accessManager . AdminId ,
2024-01-19 03:29:39 +01:00
text : $"ACM: { update.Message.From.Id }\nL'utente { update.Message.From.FirstName } { update.Message.From.LastName } @{ update.Message.From.Username }\nHa iniziato a usare il bot."
) ;
}
}
//if (!scores.Keys.Contains(uid))
//{
// await botClient.SendTextMessageAsync(
// chatId: _acm.AdminId,
// text: $"ACM: { update.Message.From.Id }\nL'utente { update.Message.From.FirstName } { update.Message.From.LastName } @{ update.Message.From.Username }\nHa iniziato a usare il bot."
// );
//}
if ( update . Type ! = UpdateType . Message )
return ;
if ( update . Message ! . Type ! = MessageType . Text )
return ;
if ( update . Message . Text . StartsWith ( "/qsc" ) )
{
int number = 1 ;
if ( update . Message . Text . Length > 4 ) int . TryParse ( update . Message . Text . Substring ( 5 ) , out number ) ;
var mostCorrect = _questionStats . GroupBy ( e = > e . Value . Correct ) . ToDictionary ( e = > e . Key , t = > t . Select ( r = > r . Key ) . ToArray ( ) ) ;
var c = 0 ;
foreach ( var item in mostCorrect . Keys . OrderByDescending ( i = > i ) )
{
if ( c = = number ) break ;
var msg = mostCorrect [ item ] . Select ( q = > q . Quest . Substring ( 0 , 30 ) ) . Aggregate ( ( a , b ) = > a + "\n\n" + b ) ;
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : $"✅ Risposte indovinate {item} volte:\n{msg}" ) ;
2024-01-19 03:29:39 +01:00
c + + ;
}
return ;
}
if ( update . Message . Text . StartsWith ( "/qsw" ) )
{
int number = 1 ;
if ( update . Message . Text . Length > 4 ) int . TryParse ( update . Message . Text . Substring ( 5 ) , out number ) ;
var mostWrong = _questionStats . GroupBy ( e = > e . Value . Wrong ) . ToDictionary ( e = > e . Key , t = > t . Select ( r = > r . Key ) . ToArray ( ) ) ;
var c = 0 ;
foreach ( var item in mostWrong . Keys . OrderByDescending ( i = > i ) )
{
if ( c = = number ) break ;
var msg = mostWrong [ item ] . Select ( q = > q . Quest . Substring ( 0 , 30 ) ) . Aggregate ( ( a , b ) = > a + "\n\n" + b ) ;
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : $"❌ Risposte sbagliate {item} volte:\n{msg}" ) ;
2024-01-19 03:29:39 +01:00
c + + ;
}
return ;
}
if ( update . Message . Text . StartsWith ( "/qsb" ) )
{
int number = 1 ;
if ( update . Message . Text . Length > 4 ) int . TryParse ( update . Message . Text . Substring ( 5 ) , out number ) ;
var mostBlank = _questionStats . GroupBy ( e = > e . Value . Blank ) . ToDictionary ( e = > e . Key , t = > t . Select ( r = > r . Key ) . ToArray ( ) ) ;
var c = 0 ;
foreach ( var item in mostBlank . Keys . OrderByDescending ( i = > i ) )
{
if ( c = = number ) break ;
var msg = mostBlank [ item ] . Select ( q = > q . Quest . Substring ( 0 , 30 ) ) . Aggregate ( ( a , b ) = > a + "\n\n" + b ) ;
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : $"🟡 Risposte non date {item} volte:\n{msg}" ) ;
2024-01-19 03:29:39 +01:00
c + + ;
}
return ;
}
if ( update . Message . Text . Equals ( "/rsp" ) )
{
if ( ! _playedQuestions . ContainsKey ( uid ) )
{
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : "❌ Non c'è niente da eliminare!" ) ;
2024-01-19 03:29:39 +01:00
return ;
}
_playedQuestions [ uid ] . Clear ( ) ;
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : "✅ Memoria eliminata!" ) ;
2024-01-19 03:29:39 +01:00
return ;
}
if ( ( ! _playingQuestions . ContainsKey ( uid ) ) | | update . Message . Text = = GetName ( ) . ToLower ( ) | | update . Message . Text = = "/" + GetName ( ) . ToLower ( ) | | update . Message . Text = = "/reset" | | update . Message . Text = = "/restart" )
{
if ( _scores . ContainsKey ( update . Message . From . Id ) ) _scores [ update . Message . From . Id ] = new OttoScore ( ) ;
else _scores . Add ( update . Message . From . Id , new OttoScore ( ) ) ;
SendRandomQuestion ( update . Message . From . Id , botClient , cancellationToken ) ;
return ;
}
var cur = _playingQuestions [ uid ] ;
var wrongMsg = "🟡 La risposta corretta era la " + ( cur . Correct + 1 ) + " ☹️" ;
if ( update . Message . Text = = "n" | | update . Message . Text = = "Passa" )
{
_scores [ uid ] . Blank + = 1 ;
_questionStats [ cur ] . Blank + = 1 ;
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : wrongMsg
2024-01-19 03:29:39 +01:00
) ;
SendRandomQuestion ( uid , botClient , cancellationToken ) ;
return ;
}
//todo try parse
var pick = - 1 ;
if ( ! int . TryParse ( update . Message . Text , out pick ) )
{
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : "❓ scusa, non ho capito 😭" ) ;
2024-01-19 03:29:39 +01:00
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : "⭕️ per uscire da 8linux, scrivi /leave" ) ;
2024-01-19 03:29:39 +01:00
return ;
}
pick - = 1 ;
if ( pick = = _playingQuestions [ uid ] . Correct )
{
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : "✅ Risposta esatta!" ) ;
2024-01-19 03:29:39 +01:00
_scores [ uid ] . Correct + = 1 ;
_questionStats [ cur ] . Correct + = 1 ;
}
else
{
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : "❌ Risposta errata!" ) ;
2024-01-19 03:29:39 +01:00
await botClient . SendTextMessageAsync (
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : wrongMsg ) ;
2024-01-19 03:29:39 +01:00
_scores [ uid ] . Wrong + = 1 ;
_questionStats [ cur ] . Wrong + = 1 ;
}
SendStats ( uid , botClient , cancellationToken ) ;
await Task . Delay ( 400 ) ;
SendRandomQuestion ( uid , botClient , cancellationToken ) ;
}
private async void SendRandomQuestion ( long uid , ITelegramBotClient botClient , CancellationToken cancellationToken )
{
var qst = PickRandomQuestion ( uid , botClient ) ;
2024-01-21 14:04:11 +01:00
try
{
2024-01-22 17:35:01 +01:00
if ( qst . Quest . Length < = 40 )
{
Console . WriteLine ( "Sto inviando la domanda " + qst . Quest + " a " + uid ) ;
}
else
{
Console . WriteLine ( "Sto inviando la domanda " + qst . Quest . Substring ( 0 , 40 ) + " a " + uid ) ;
}
2024-01-21 14:04:11 +01:00
}
catch ( Exception e )
{
botClient . SendTextMessageAsync (
chatId : _accessManager . AdminId ,
2024-01-22 17:35:01 +01:00
text : $"Question is malformed -> {qst.Quest} \n {e.Message}"
2024-01-21 14:04:11 +01:00
) ;
return ;
}
2024-01-19 03:29:39 +01:00
if ( ! _questionStats . ContainsKey ( qst ) ) _questionStats . Add ( qst , new OttoScore ( ) ) ;
if ( _playingQuestions . ContainsKey ( uid ) ) _playingQuestions [ uid ] = qst ;
else _playingQuestions . Add ( uid , qst ) ;
string answers = "" ;
List < KeyboardButton > kbs = new List < KeyboardButton > ( ) ;
bool splitAns = false ;
foreach ( var ans in qst . Answers )
if ( ( ans . Contains ( "\n" ) & & ans . Substring ( ans . IndexOf ( "\n" ) ) . Length > 1 ) | | ans . Contains ( "img=" ) ) splitAns = true ;
var corry = qst . Answers [ qst . Correct ] ;
for ( int i = 0 ; i < qst . Answers . Count ; i + + )
{
if ( ! splitAns ) answers + = ( i + 1 ) + ". " + qst . Answers [ i ] + "\n\n" ;
kbs . Add ( ( i + 1 ) . ToString ( ) ) ;
}
KeyboardButton [ ] fr = kbs . ToArray ( ) ;
ReplyKeyboardMarkup replyKeyboardMarkup = new ( new [ ]
{
fr ,
new KeyboardButton [ ] { "Passa" } ,
} )
{
ResizeKeyboard = true
} ;
string quest = qst . Quest ;
2024-01-31 13:01:37 +01:00
if ( ! string . IsNullOrEmpty ( qst . Image ) )
2024-01-31 04:56:23 +01:00
{
try
{
2024-01-31 12:49:28 +01:00
if ( qst . Image . Contains ( "http" ) )
{
await botClient . SendPhotoAsync (
chatId : uid ,
photo : qst . Image ) ;
}
else
{
await botClient . SendPhotoAsync (
chatId : uid ,
photo : File . OpenRead ( _imgBaseDir + "/" + qst . Image ) ) ;
}
2024-01-31 04:56:23 +01:00
}
catch ( Exception e )
{
CatchParsingError ( botClient , quest , uid , e ) ;
}
}
2024-01-19 03:29:39 +01:00
if ( qst . Quest . StartsWith ( "img=" ) )
{
try
{
2024-01-31 12:49:28 +01:00
if ( qst . Image . Contains ( "http" ) )
{
await botClient . SendPhotoAsync (
chatId : uid ,
photo : quest . Substring ( 4 ) . Split ( '\n' ) [ 0 ] ) ;
}
else
{
await botClient . SendPhotoAsync (
chatId : uid ,
photo : File . OpenRead ( _imgBaseDir + "/" + quest . Substring ( 4 ) . Split ( '\n' ) [ 0 ] ) ) ;
}
2024-01-19 03:29:39 +01:00
}
catch ( Exception e )
{
CatchParsingError ( botClient , quest , uid , e ) ;
}
quest = quest . Substring ( quest . IndexOf ( '\n' ) + 1 ) ;
}
try
{
Message sentMessage = await botClient . SendTextMessageAsync (
chatId : uid ,
text : "📎 " + PrepareHtml ( quest ) + "\n\n" + PrepareHtml ( answers ) ,
replyMarkup : replyKeyboardMarkup ,
parseMode : ParseMode . Html
) ;
}
catch ( Exception e ) { CatchParsingError ( botClient , quest , uid , e ) ; }
if ( splitAns )
{
for ( int i = 0 ; i < qst . Answers . Count ; i + + )
{
await Task . Delay ( 350 ) ;
if ( qst . Answers [ i ] . StartsWith ( "img=" ) )
{
try
{
2024-01-31 12:49:28 +01:00
if ( qst . Image . Contains ( "http" ) )
{
await botClient . SendPhotoAsync (
chatId : uid ,
photo : qst . Answers [ i ] . Split ( '\n' ) [ 0 ] . Substring ( 4 ) ) ;
}
else
{
await botClient . SendPhotoAsync (
chatId : uid ,
photo : File . OpenRead ( _imgBaseDir + "/" + qst . Answers [ i ] . Split ( '\n' ) [ 0 ] . Substring ( 4 ) ) ) ;
}
2024-01-19 03:29:39 +01:00
}
catch ( Exception e )
{
CatchParsingError ( botClient , "[R] " + qst . Answers [ i ] . Substring ( 0 , 20 ) + " -> " + quest , uid , e ) ;
}
}
else
{
Message sentMessage = await botClient . SendTextMessageAsync (
chatId : uid ,
text : "✏️ Risposta " + PrepareHtml ( ( i + 1 ) + ":\n" + qst . Answers [ i ] ) ,
replyMarkup : replyKeyboardMarkup ,
parseMode : ParseMode . Html
) ;
}
}
}
}
private async void CatchParsingError ( ITelegramBotClient botClient , string quest , long uid , Exception e )
{
await botClient . SendTextMessageAsync (
chatId : uid ,
text : "❌ Cercavo di inviarti una domanda ma si è verificato un errore anomalo nel parsing. Per favore, invia /restart per resettarmi.\nL'errore verrà automaticamente segnalato (visto che tecnologia avanzatissima?)"
) ;
await botClient . SendTextMessageAsync (
chatId : uid ,
text : $"❌ La domanda {quest.Substring(0, 60)} è rotta.\nSi è verificato {e.Message}"
) ;
}
private static string PrepareHtml ( string s )
{
return s . Replace ( "<" , "<" )
. Replace ( ">" , ">" )
. Replace ( "<code>" , "<code>" )
. Replace ( "</code>" , "</code>" )
. Replace ( "<pre>" , "<pre>" )
. Replace ( "</pre>" , "</pre>" )
. Replace ( "<b>" , "<b>" )
. Replace ( "</b>" , "</b>" ) ;
}
private async void SendStats ( long uid , ITelegramBotClient botClient , CancellationToken cancellationToken )
{
var stats = _scores [ uid ] ;
var total = stats . Correct + stats . Wrong + stats . Blank ;
2024-01-22 17:35:01 +01:00
await botClient . SendTextMessageAsync (
2024-01-19 03:29:39 +01:00
chatId : uid ,
2024-01-22 17:35:01 +01:00
text : stats . Correct + " corrette (" + ( ( float ) stats . Correct / ( float ) total ) * 100f + "%)\n" + stats . Wrong + " errate (" + ( ( float ) stats . Wrong / ( float ) total ) * 100f + "%)\n" + stats . Blank + " non date (" + ( ( float ) stats . Blank / ( float ) total ) * 100f + "%)\n" ) ;
2024-01-19 03:29:39 +01:00
}
public string GetName ( )
{
return _name ;
}
public List < Question > GetQuestions ( )
{
return _questions ;
}
}
}