Wir brechen in Top10 aus. Telegrammbot
Hintergrund
Alles begann damit, dass sie mir einen Link zu einem Bot in Telegram mit einem Angebot zum Spielen schickten.
Es sieht ungefähr so aus.

Nach meinem ersten Spiel habe ich 28 Punkte verdient, kein sehr beeindruckendes Ergebnis. Sie brauchen also überhaupt nichts - ein Programm, das Wörter aus den Buchstaben des Originalworts findet, und eine Datenbank mit Substantiven russischer Wörter.
Lass uns gehen
Ich habe beschlossen, sqlite3 für die Datenbank zu verwenden, es ist mobil und für diese Aufgabe am meisten.
Die Struktur der Basis sieht so aus.
CREATE TABLE IF NOT EXISTS words ( word VARCHAR(225) UNIQUE NOT NULL, length INTEGER NOT NULL );
- Wort - Aus dem Namen geht hervor, dass dies die gespeicherte wörtliche Bedeutung des Wortes ist.
- Länge - Zeichenlänge.
Es gibt eine Struktur, um sie zu füllen, habe ich eine Liste von Substantiven russischer Wörter verwendet .
Nachdem die Datenbank gefüllt und nach Wörtern gesucht wurde, wurde beschlossen, die Verarbeitung der Flags in einem Code zu implementieren.
Die Erstellung der Basisdatei und die Erstellung der Tabelle sind ebenfalls in init () implementiert.
func init() { var err error connection, err = sql.Open("sqlite3", "./words.db") if err != nil { log.Fatalf("Failed connection: %v", err) } _, err = connection.Exec(`CREATE TABLE IF NOT EXISTS words (word VARCHAR(225) UNIQUE NOT NULL, length INTEGER NOT NULL);`) if err != nil { log.Fatalf("Failed create database table words: %v", err) } }
Insert () Funktion
Wenn Sie Wörter hinzufügen, müssen Sie berücksichtigen, dass wir das kyrillische Alphabet verwenden. Aus diesem Grund passt die übliche len()
Funktion nicht zu uns. Wir verwenden utf8.RuneCountInString()
, um die Wortlänge korrekt zu berechnen.
Fügen Sie eine Fehlerprüfung hinzu, if err.Error() != "UNIQUE constraint failed: words.word"
- erforderlich, um neue Wörterbücher einzuführen, die eine Kopie der Wörter aus der Datenbank enthalten.
func insert(word string) error { _, err := connection.Exec("INSERT INTO words (word,length) VALUES(?,?)", word, utf8.RuneCountInString(word)) if err != nil && err.Error() != "UNIQUE constraint failed: words.word" { return err } return nil }
Um nach Wörtern zu suchen, aus denen die Quelle besteht, müssen Sie sie in Buchstaben zerlegen. Ein Wort kann mehrere identische Buchstaben enthalten, um den Betrag zu berechnen, den wir verwenden map[rune]int
wobei int
die Anzahl der im Wort gefundenen Buchstaben ist.
func decay(word string) map[rune]int { var m = make(map[rune]int) for _, char := range word { m[char]++ } return m }
Die Suche selbst wird im Multithread-Modus durchgeführt, wobei die Anzahl der Gorutinen = die Länge des ursprünglichen Wortes abzüglich einer Gorutine seitdem ist Wir beginnen mit der Suche nach Wörtern, die aus zwei oder mehr Buchstaben bestehen.
Bei diesem Ansatz arbeitete das Programm zu schnell und schickte die Anzahl der Antworten = Gorutine an den Chat an den Bot, obwohl es Zeit gab. time.Sleap(1 * time.Second)
in jeder Gorutine - dies führte dazu, dass mein Telegramm für 10 Minuten von allen Geräten blockiert wurde. Ich habe dies berücksichtigt und in der aktuellen Version eine Verzögerung für das Senden festgelegt und die Sendefunktion selbst in eine separate Gorutine gestellt, die über einen gemeinsamen Kanal mit den anderen kommuniziert. Die Suche wird wie bisher durchgeführt.
Wir verwenden waitGroup{}
als Mechanismus, um die Suche nach allen Wörtern aus der Datenbank zu beenden und dann den Kanal zu schließen.
func findSubWords(word string) { list := decay(word) for length := 2; length <= utf8.RuneCountInString(word); length++ { wg.Add(1) go func(out chan<- string, length int) { search(out, list, length) wg.Done() fmt.Println("Done: ", length) }(out, length) } wg.Wait() fmt.Println("search done") close(out) }
Die Suchfunktion wählt aus der Datenbank alle Wörter mit der gewünschten Länge aus und durchläuft die Schleife, um zu prüfen, ob das Wort geeignet ist. Die Überprüfung erfolgt in mehreren Schritten. Aufgrund der Verwendung der map
erstellen wir jedes Mal, wenn wir den Schleifenzyklus abschließen, eine neue Kopie. Wir benötigen eine Kopie der map
, um die Anzahl der Buchstaben in einem Wort zu überprüfen. Jedes Mal, wenn ein Buchstabe übereinstimmt, verringern wir den Wert um einen Schlüssel um eins, bis er auf Null abfällt. Wenn ein solcher Buchstabe den Wert 0 hat, weisen wir ontain=false
Variablen und ontain=false
Variable ontain=false
Wenn der Zyklus endet, wird das Wort nicht zum Kanal hinzugefügt.
func search(out chan<- string, wordRuneList map[rune]int, length int) { wordList, err := selects(length) if err != nil { log.Printf("fail length %v, error: %v", length, err) } for _, word := range wordList { var ( wordCopyList = make(map[rune]int) contain = true ) for k, v := range wordRuneList { wordCopyList[k] = v } for _, r := range word { if _, ok := wordCopyList[r]; ok && wordCopyList[r] > 0 { wordCopyList[r]-- } else { contain = false break } } if contain { out <- word } } }
Es bleibt der Fall für kleine, so dass das Programm selbst die Antworten an den Chat sendet. Da der Bot nicht mit einem anderen Bot kommunizieren kann, musste ich mein persönliches Konto verwenden. Ich habe mich für einen Open Source Client entschieden .
Ausführen auf dem Port: 9090. Wir senden Nachrichten an den Chat an den Bot.
func send(in <-chan string) { conn, _ := net.Dial("tcp", "localhost:9090")
Befehle zum schnellen Starten von telegram-cli auf debian.
Installieren Sie die erforderlichen Bibliotheken.
sudo apt install libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev libjansson-dev libpython-dev libgcrypt20 libz-dev make git
Klonen eines Repositorys.
git clone --recursive https://github.com/vysheng/tg.git && cd tg
Die Ausführung der Konfiguration.
./configure make
Client-Start auf Port 9090
bin/telegram-cli -P 9090
Damit der Client den Bot finden kann, muss der search WordsGame-bot
bereits im Client ausgeführt und das Ergebnis mit dem Befehl msg WordsGame-bot test
werden. Wenn Sie nach den Aktionen den msg WordsGame-bot test
nicht in den Chat-Chat geschrieben haben, versuchen Sie, das Spiel persönlich damit zu spielen.
Vergessen Sie nicht, sich anzumelden, damit der Kunde mit der Arbeit beginnen kann. Er schlägt vor, wenn Sie sich zum ersten Mal anmelden.
Alles scheint fertig zu sein. Das Programm kann die Basis füllen und das Spiel mit dem Bot spielen, aber nur, wenn Sie selbst nach Worten vom Bot fragen.
Aber das alles ist langsam, aber wir wollen sofort die erste Zeile nehmen, und dafür müssen wir dem Programm beibringen, Wörter vom Bot anzufordern. Wir werden eine Verbindung herstellen und den Befehl msg WordsGame-bot /play
senden. Der Bot hat eine Verzögerung, also warten wir 5 Sekunden. Danach fordern wir die letzte Nachricht aus der Geschichte mit dem history WordsGame-bot 1
Dies ist die Antwort bzw. das Wort, das wir als Quelle verwenden sollten. Um aus conn
zu lesen conn
erstellen Sie die Variable reply = make([]byte, 512)
. Nachdem wir die ganze Antwort mit onn
sieht es onn
so aus.
history @manymanywords_bot 1 ANSWER 58 [16:10] WordsGame-bot »»»
Erstellen Sie regexp.MustCompile("([-]{1,100})")
, um nach Wörtern aus dem kyrillischen Alphabet zu suchen. Danach wählen wir unser Wort.
else if *god { go send(out) for { var ( conn, _ = net.Dial("tcp", "localhost:9090")
Aber es gibt ein Problem, weil Wir haben den Kanal geschlossen, nachdem wir alle Wörter gefunden hatten. Um dies zu beheben, benötigen wir die globale Variable GODMOD
. Fügen Sie findSubWords
eine Bedingung findSubWords
. Wenn wir nun den Schalter -g verwenden, wird die Variable GODMOD auf true gesetzt und der Kanal wird nicht geschlossen. Nach Abschluss der Schleife fordern wir ein neues Wort an.
if !GODMOD { close(out) }
Jetzt können Sie sich das Ergebnis ansehen.

Nützliche Links