From f5f4ca13af25753dcbd8c359fa62b6a8ad8ce120 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Mon, 20 Oct 2025 22:06:36 +0200 Subject: [PATCH 01/42] Starting work on Captcha --- action.php | 55 +- conf/default.php | 2 + conf/metadata.php | 3 + lang/de/wordlist.txt | 2000 ++++++++++++++++++++++++++++++++++++++++++ lang/en/settings.php | 5 +- lang/en/wordlist.txt | 2000 ++++++++++++++++++++++++++++++++++++++++++ lang/fr/wordlist.txt | 1972 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 6028 insertions(+), 9 deletions(-) create mode 100644 lang/de/wordlist.txt create mode 100644 lang/en/wordlist.txt create mode 100644 lang/fr/wordlist.txt diff --git a/action.php b/action.php index 0655bd1..b3536e8 100644 --- a/action.php +++ b/action.php @@ -30,6 +30,11 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertAdminHeader'); } + // Override the page rendering, if a captcha needs to be displayed: + if ($ACT !== 'admin') { + $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'showCaptcha'); + } + // write to the log after the page content was displayed: $controller->register_hook('TPL_CONTENT_DISPLAY', 'AFTER', $this, 'writeServerLog'); @@ -38,7 +43,6 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { /* session information */ private $sessionId = null; private $sessionType = ''; - private $ipAddress = null; /** * Inserts tracking code to the page header @@ -107,7 +111,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { // create the log array: $logArr = Array( - $_SERVER['REMOTE_ADDR'], /*$this->ipAddress, // remote IP */ + $_SERVER['REMOTE_ADDR'], /* remote IP */ $pageId, /* page ID */ $this->sessionId, /* Session ID */ $this->sessionType, /* session ID type */ @@ -140,7 +144,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { private function getCountryCode() { - $country = ( $this->ipAddress == 'localhost' ? 'local' : 'ZZ' ); // default if no geoip is available! + $country = ( $_SERVER['REMOTE_ADDR'] == '127.0.0.1' ? 'local' : 'ZZ' ); // default if no geoip is available! $lib = $this->getConf('geoiplib'); /* which library to use? (can only be phpgeoip or disabled) */ @@ -160,9 +164,6 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { private function getSessionInfo() { - $this->ipAddress = $_SERVER['REMOTE_ADDR'] ?? null; - if ($this->ipAddress == '127.0.0.1' || $this->ipAddress == '::1') $this->ipAddress = 'localhost'; - // what is the session identifier? if (isset($_SESSION)) { $sesKeys = array_keys($_SESSION); /* DokuWiki Session ID preferred */ @@ -178,8 +179,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $this->sessionId = session_id(); $this->sessionType = 'php'; } - if (!$this->sessionId && $this->ipAddress) { /* no PHP session ID, try IP address */ - $this->sessionId = $this->ipAddress; + if (!$this->sessionId) { /* no PHP session ID, try IP address */ + $this->sessionId = $_SERVER['REMOTE_ADDR']; $this->sessionType = 'ip'; } if (!$this->sessionId) { /* if everything else fails, just us a random ID */ @@ -187,4 +188,42 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $this->sessionType = 'rand'; } } + + public function showCaptcha(Event $event) { + + if ($this->getConf('useCaptcha') && $this->checkCaptchaCookie()) { + + $event->preventDefault(); // don't show normal content + $this->insertDadaFiller(); // show dada filler instead! + $this->insertCaptchaLoader(); // and load the captcha + + } else { + echo '

Normal page.

'; + } + } + + private function checkCaptchaCookie() { + + $cookieVal = isset($_COOKIE['_c_']) ? $_COOKIE['_c_'] : ''; + $seed = $this->getConf('captchaSeed'); + + return ($cookieVal == $seed ? 0 : 1); // #TODO: encrypt with other data + } + + private function insertCaptchaLoader() { + + } + + private function insertDadaFiller() { + // #TODO: make a dada filler + + echo '

'; tpl_pagetitle(); echo "

\n"; + + echo ''; + + echo "

Placeholder text while the captcha is being displayed.

\n"; + + + } + } \ No newline at end of file diff --git a/conf/default.php b/conf/default.php index 1ba3fbd..48f2cf5 100644 --- a/conf/default.php +++ b/conf/default.php @@ -6,3 +6,5 @@ */ $conf['geoiplib'] = 'disabled'; +$conf['useCaptcha'] = 0; +$conf['captchaSeed'] = 'b472719ba5634d378a7d7f9bfc46659f'; diff --git a/conf/metadata.php b/conf/metadata.php index 409716f..1c02879 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -7,3 +7,6 @@ $meta['geoiplib'] = array('multichoice', '_choices' => array ('disabled', 'phpgeoip')); + +$meta['useCaptcha'] = array('onoff'); +$meta['captchaSeed'] = array('string', '_pattern' => '/[\da-fA-F]{16,32}/'); diff --git a/lang/de/wordlist.txt b/lang/de/wordlist.txt new file mode 100644 index 0000000..1b968f2 --- /dev/null +++ b/lang/de/wordlist.txt @@ -0,0 +1,2000 @@ +der +die +und +in +den +von +zu +das +mit +sich +des +auf +für +ist +im +dem +nicht +ein +eine +als +auch +es +an +werden +aus +er +hat +daß +sie +nach +wird +bei +einer +um +am +sind +noch +wie +einem +über +einen +so +Sie +zum +war +haben +nur +oder +aber +vor +zur +bis +mehr +durch +man +sein +wurde +sei +Prozent +hatte +kann +gegen +vom +können +schon +wenn +habe +seine +Mark +ihre +dann +unter +wir +soll +ich +eines +Es +Jahr +zwei +Jahren +diese +dieser +wieder +keine +Uhr +seiner +worden +will +zwischen +immer +Millionen +was +sagte +Er +gibt +alle +diesem +seit +muß +wurden +beim +doch +jetzt +waren +drei +Jahre +neue +neuen +damit +bereits +da +ihr +seinen +müssen +ab +ihrer +ohne +sondern +selbst +ersten +nun +etwa +heute +ihren +weil +ihm +seien +Menschen +Deutschland +anderen +werde +sagt +rund +Aber +ihn +Ende +jedoch +Zeit +sollen +ins +Wenn +seinem +uns +Stadt +geht +sehr +hier +ganz +erst +wollen +Berlin +vor allem +sowie +hatten +kein +deutschen +machen +lassen +Als +Unternehmen +andere +ob +dieses +steht +dabei +wegen +weiter +denn +beiden +einmal +etwas +nichts +allerdings +vier +gut +viele +wo +viel +dort +alles +wäre +SPD +kommt +vergangenen +denen +fast +fünf +könnte +hätten +Frau +Am +dafür +kommen +diesen +letzten +zwar +großen +dazu +Mann +sollte +würde +also +bisher +Leben +Milliarden +Welt +Regierung +konnte +ihrem +Frauen +während +Land +zehn +würden +stehen +ja +USA +heißt +dies +zurück +Kinder +dessen +ihnen +deren +sogar +Frage +gewesen +erste +gab +liegt +gar +davon +gestern +geben +Teil +Polizei +dass +hätte +eigenen +kaum +sieht +große +Denn +weitere +sehen +macht +Angaben +weniger +gerade +läßt +Geld +München +deutsche +allen +darauf +wohl +später +könne +deshalb +aller +kam +Arbeit +mich +gegenüber +nächsten +bleibt +wenig +lange +gemacht +Fall +mir +gehen +Berliner +mal +Weg +CDU +wollte +sechs +keinen +Woche +dagegen +alten +möglich +gilt +erklärte +müsse +könnten +Geschichte +zusammen +finden +Tag +Art +erhalten +Man +Dollar +Wochen +jeder +nie +bleiben +besonders +Jahres +Deutschen +Den +Zu +zunächst +derzeit +allein +deutlich +Entwicklung +weiß +einige +sollten +Präsident +geworden +statt +Bonn +Platz +inzwischen +Nur +Freitag +pro +seines +Montag +Europa +schließlich +Sonntag +einfach +gehört +eher +oft +Zahl +neben +hält +weit +Partei +meisten +Thema +zeigt +Politik +Aus +zweiten +Januar +insgesamt +je +mußte +Anfang +hinter +ebenfalls +ging +Mitarbeiter +darüber +vielen +Ziel +darf +Seite +fest +hin +erklärt +Namen +Haus +An +Frankfurt +Gesellschaft +Mittwoch +damals +Dienstag +Hilfe +Mai +Markt +Tage +Donnerstag +halten +gleich +nehmen +solche +Entscheidung +besser +alte +Leute +Ergebnis +Samstag +sagen +System +März +tun +Monaten +kleinen +lang +knapp +bringen +wissen +Kosten +Erfolg +bekannt +findet +daran +künftig +wer +acht +Grünen +schnell +Grund +scheint +Zukunft +bin +liegen +politischen +Gruppe +Rolle +stellt +Juni +sieben +September +nämlich +Männer +Oktober +Mrd. +überhaupt +eigene +gegeben +Stunden +eigentlich +Meter +ließ +Probleme +vielleicht +ebenso +Bereich +zum Beispiel +Höhe +Familie +Bild +Ländern +Informationen +Frankreich +Tagen +schwer +zuvor +genau +April +stellen +neu +erwartet +Hamburg +sicher +führen +Mal +mehrere +Wirtschaft +Mio +Programm +offenbar +Hier +weiteren +natürlich +konnten +stark +Dezember +Juli +ganze +kommenden +Kunden +bekommen +eben +kleine +trotz +wirklich +Lage +Länder +leicht +gekommen +Spiel +laut +November +kurz +politische +führt +innerhalb +meint +immer wieder +Form +Münchner +AG +anders +ihres +völlig +beispielsweise +gute +bislang +August +Hand +jede +GmbH +Film +Minuten +erreicht +beide +Musik +Kritik +Mitte +Verfügung +Buch +dürfen +jeweils +einigen +Umsatz +spielen +Daten +welche +müßten +hieß +paar +nachdem +Kunst +Euro +gebracht +Problem +jeden +Ihre +Sprecher +recht +erneut +längst +europäischen +Sein +Eltern +Beginn +besteht +Seine +mindestens +machte +Jetzt +bietet +außerdem +Bürger +Trainer +bald +Deutsche +Fragen +klar +Seiten +gehören +erstmals +Februar +zeigen +Titel +Stück +größten +FDP +setzt +Wert +Frankfurter +Staat +möchte +daher +wolle +Bundesregierung +lediglich +Nacht +Krieg +Opfer +Tod +nimmt +Firma +zuletzt +Werk +hohen +leben +unter anderem +Kirche +weiterhin +gebe +gestellt +Mitglieder +Rahmen +zweite +Paris +Situation +gefunden +Wochenende +internationalen +Wasser +Recht +sonst +stand +Hälfte +Möglichkeit +versucht +blieb +junge +Mehrheit +Straße +Sache +arbeiten +Monate +Mutter +berichtet +letzte +Gericht +wollten +Ihr +zwölf +zumindest +Wahl +genug +Weise +Vater +Bericht +amerikanischen +hoch +beginnt +Wort +obwohl +Kopf +spielt +Interesse +Westen +verloren +Preis +jedem +erreichen +setzen +spricht +früher +teilte +Landes +zudem +einzelnen +bereit +Blick +Druck +Bayern +Kilometer +gemeinsam +Bedeutung +Chance +Politiker +Zwei +besten +Ansicht +endlich +Stelle +direkt +Bevölkerung +solchen +Alle +solle +jungen +Einsatz +richtig +größte +sofort +neuer +ehemaligen +unserer +dürfte +schaffen +Augen +Rußland +Internet +Raum +Mannschaft +neun +kamen +Ausstellung +Zeiten +Dem +einzige +meine +Nun +Verfahren +Angebot +Richtung +Projekt +niemand +Kampf +weder +tatsächlich +Personen +Heute +geführt +Gespräch +Kreis +Hamburger +Schule +guten +Hauptstadt +durchaus +Zusammenarbeit +darin +Amt +Schritt +meist +groß +zufolge +Sprache +Region +Punkte +Vergleich +genommen +gleichen +du +Ob +Soldaten +Universität +verschiedenen +Kollegen +neues +Bürgermeister +Angst +stellte +Sommer +danach +anderer +gesagt +Sicherheit +Macht +Bau +handelt +Folge +Bilder +lag +Osten +Handel +sprach +Aufgabe +Chef +frei +dennoch +DDR +hohe +Firmen +bzw +Koalition +Mädchen +entwickelt +fand +Diskussion +bringt +Hause +Gefahr +per +zugleich +früheren +dadurch +ganzen +abend +erzählt +Streit +Vergangenheit +Parteien +Verhandlungen +jedenfalls +gesehen +französischen +Trotz +darunter +Spieler +forderte +Beispiel +Meinung +wenigen +Publikum +sowohl +meinte +mag +Auto +Lösung +Boden +Präsidenten +hinaus +verletzt +weltweit +Sohn +bevor +Peter +mußten +keiner +Produktion +Ort +braucht +Zusammenhang +Kind +Verein +sprechen +Aktien +gleichzeitig +London +sogenannten +Richter +geplant +Italien +Mittel +her +freilich +Mensch +großer +Bonner +wenige +öffentlichen +Unterstützung +dritten +nahm +Bundesrepublik +Arbeitsplätze +bedeutet +Feld +Dr. +Bank +oben +gesetzt +Ausland +Ministerpräsident +Vertreter +z.B. +jedes +ziehen +Parlament +berichtete +China +aufgrund +Stellen +warum +Kindern +heraus +heutigen +Anteil +Herr +Öffentlichkeit +Abend +Liebe +rechnen +fällt +New York +Industrie +Stuttgarter +wären +Vorjahr +Sicht +Idee +Banken +verlassen +Leiter +Bühne +insbesondere +offen +stets +Theater +ändern +entschieden +Staaten +Experten +Gesetz +Geschäft +Tochter +angesichts +gelten +Mehr +erwarten +läuft +fordert +Japan +Sieg +Stimmen +wählen +russischen +gewinnen +CSU +bieten +Nähe +jährlich +Bremen +Schüler +Rede +Funktion +Zuschauer +hingegen +anderes +Führung +Besucher +Drittel +Moskau +immerhin +Vorsitzende +Urteil +Kultur +betonte +mittlerweile +Saison +Konzept +suchen +Zahlen +Roman +Gewalt +Köln +gesamte +indem +EU +Stunde +ehemalige +Auftrag +entscheiden +genannt +tragen +Börse +langen +häufig +Chancen +vor allem +Position +alt +Luft +Studenten +übernehmen +stärker +ohnehin +zeigte +geplanten +Reihe +darum +verhindern +begann +Medien +verkauft +Minister +wichtig +amerikanische +sah +gesamten +einst +verwendet +vorbei +Behörden +helfen +Folgen +bezeichnet +Ihnen +zur Zeit +voll +deutscher +Worten +plötzlich +müßte +Vertrag +Staatsanwaltschaft +Monat +Oder +Herbst +Israel +zahlen +Zeitung +Grenzen +Wissenschaftler +Partner +Patienten +nutzen +Bund +setzte +Betrieb +Michael +beteiligt +Professor +Fernsehen +Künstler +mehreren +erinnert +Liste +Möglichkeiten +Autor +täglich +eingesetzt +Versuch +Alter +Autos +Kohl +außer +Hoffnung +Verkauf +nennt +erscheint +führte +Prozeß +Täter +bisherigen +länger +erkennen +treffen +unser +begonnen +Antrag +beschäftigt +Opposition +Maßnahmen +brachte +nächste +gezeigt +Sinn +Erde +gefordert +Wohnung +all +Menge +gerne +Hintergrund +hören +Deutschlands +selten +bestätigt +bestimmt +Statt +entstehen +nannte +schreibt +Union +brauchen +gewählt +Kraft +elf +trägt +zieht +Grenze +Geschäftsführer +Team +Gebäude +Tonnen +Wettbewerb +Anspruch +Polen +morgen +Bremer +Wegen +Gebiet +glaubt +Sa +Natur +Arbeiten +jene +Fällen +Jahrhundert +leisten +Zeitpunkt +internationale +mein +Konkurrenz +nach wie vor +nicht einmal +stieg +notwendig +sogenannte +fahren +kostet +entsprechenden +geplante +geschlossen +Fehler +Zweifel +Erklärung +wiederum +erschienen +gehe +Glück +erfolgreich +fehlt +Gruppen +Aktion +kündigte +meinen +manchmal +verschiedene +übernommen +möglichst +SZ +lieber +Verantwortung +Gespräche +Suche +gern +fallen +Organisation +Preise +weitgehend +Basis +Computer +aufgenommen +Schutz +Eindruck +fiel +nahe +schlecht +lebt +Verhältnis +Forderung +britischen +sozialen +Technik +entstanden +Vorstand +verantwortlich +of +bestimmten +jüngsten +geboren +erhält +wobei +Gegner +Gründen +Material +Spitze +Gewinn +Punkten +vertreten +Schulen +Studie +Zeichen +gewonnen +Kurs +Washington +Türkei +Erfahrungen +wirkt +Armee +erhielt +beginnen +bißchen +entfernt +vorgesehen +Ergebnisse +zahlreiche +entgegen +hielt +privaten +sucht +Gemeinde +Antwort +Zentrum +bilden +legen +Schweiz +gemeinsamen +weg +teilweise +Großbritannien +Licht +Hannover +Produkte +Stimme +diesmal +Schluß +gingen +angeboten +Gesicht +Treffen +ließen +eröffnet +versuchen +Konzern +Leistungen +Gäste +Wohnungen +möglicherweise +wichtige +zog +geraten +Bewegung +nötig +Mitglied +hundert +Düsseldorf +PDS +wahrscheinlich +vermutlich +selber +ständig +Senat +Branche +Journalisten +enthält +laufen +Straßen +Einführung +arbeitet +verlangt +Werke +Runde +Besuch +unterstützt +Manager +Brüssel +de +schweren +sitzt +geschrieben +jemand +französische +Wachstum +taz +bestimmte +Drei +Freiheit +entsprechende +kennt +Band +Arbeitgeber +glauben +Wege +verurteilt +tritt +Volk +Pläne +R +angekündigt +Satz +sitzen +Vorschlag +rechnet +muss +erhöht +Investitionen +Aufgaben +warten +entdeckt +wichtigsten +Heimat +verstehen +schrieb +trotzdem +erfahren +Unternehmens +frühere +bekommt +Jugendlichen +Dinge +öffentliche +Institut +Wirkung +sorgen +trifft +reden +Werte +Forscher +Wo +Sitzung +zusätzlich +vielmehr +Anlaß +gehalten +Tel. +Schweizer +beschlossen +Serben +Bundestag +Verwaltung +vorher +automatisch +betont +Leistung +abgeschlossen +zufrieden +Beschäftigten +Papier +Thomas +Verbindung +Sinne +ziemlich +Debatte +enthalten +ausschließlich +bestehen +Flüchtlinge +Jugendliche +Freund +A +Themen +überall +droht +Gewerkschaften +Bedingungen +Beziehungen +Text +europäische +Punkt +Erfahrung +I +Außenminister +soziale +russische +Tisch +Republik +höher +reicht +Million +gehabt +politisch +folgen +ähnlich +geschaffen +Schaden +erster +Körper +Amerikaner +getötet +Frieden +zählt +Beteiligung +Wagen +Bahn +vergessen +Verlust +Tiere +Museum +gegründet +daraus +Start +Kontrolle +Einfluß +Österreich +Norden +Wunsch +manche +Wahlen +Reise +steigen +gemeinsame +Darstellung +Demokratie +König +England +nennen +Teile +Jahrhunderts +denken +Name +Lehrer +S. +laufenden +verbunden +Qualität +zusätzliche +Widerstand +schön +Spanien +gegangen +Beamten +Europas +Freunde +mitteilte +erklären +Familien +Gerade +gefallen +Aktie +Krise +Baden-Württemberg +Nummer +wies +außerhalb +gestiegen +Reform +Literatur +Arbeitslosigkeit +Lebens +Sozialdemokraten +gewann +Moment +angeblich +entsprechend +Mill +weiteres +beträgt +dritte +wesentlich +Amerika +Insel +britische +Nachfolger +entspricht +dahin +unseren +solcher +Juden +Sport +Rathaus +verkaufen +größere +Frühjahr +gebaut +Wahrheit +dar +Kommission +Polizisten +Bosnien +offensichtlich +Grundlage +relativ +Nein +Klasse +tut +hängt +wenigstens +aktuellen +Modell +Herstellung +hinzu +Gedanken +Übernahme +entwickeln +Quartal +meiner +dürften +jener +Zustimmung +legt +fordern +Einheit +blieben +Laut +in Zukunft +Szene +Klaus +Bundeskanzler +Schwierigkeiten +geblieben +lesen +getroffen +keineswegs +Abschluß +Verlag +Interessen +Netz +anschließend +Plan +Ausbildung +befindet +zunehmend +verzichten +gespielt +ermittelt +legte +Ruhe +Hans +Händler +genutzt +höhere +starke +treten +Veranstaltung +Stimmung +Ärzte +erheblich +standen +Teilnehmer +möchten +starken +Forderungen +Stand +Anlage +siehe +erfolgt +Vertrieb +beste +gelungen +Ordnung +Andreas +lautet +kritisiert +Schröder +machten +verstärkt +Süden +Haltung +freien +weist +nahezu +Motto +abgelehnt +gelang +gezogen +frühen +schreiben +aufs +gleiche +öffentlich +Häuser +kleiner +folgt +Einer +Ja +Wien +möglichen +weiterer +kaufen +Gefühl +wirtschaftlichen +Initiative +hofft +dürfe +Person +Telekom +kennen +interessiert +Beitrag +Geschäftsjahr +lernen +gefragt +bezeichnete +Meister +Bücher +kürzlich +Tradition +einzelne +Vorsitzender +Verband +Grüne +überzeugt +Rückkehr +schwere +falsch +unten +behandelt +indes +großes +Worte +getan +Vorstellung +somit +langsam +gedacht +geändert +hervor +Hessen +Städte +unbedingt +Hotel +Artikel +tätig +zahlreichen +Geburtstag +gesprochen +schließen +Engagement +Gründe +erlaubt +Ausländer +Anleger +größer +verlieren +Verhalten +ausgesprochen +fanden +Betriebe +Praxis +ums +galt +Gemeinden +Kanzler +Viertel +private +Zustand +miteinander +genauso +präsentiert +Planung +Forschung +staatlichen +Gott +einzigen +wichtigen +kosten +Verkehr +befinden +Kauf +Vorstellungen +Halle +Untersuchung +bessere +unserem +jeweiligen +angelegt +beendet +Fälle +Einschätzung +stehe +Dienst +herrscht +Begriff +Sekunden +Essen +ans +erfüllt +Krankenhaus +Auskunft +bauen +Rechnung +Arbeitnehmer +endgültig +Truppen +Jungen +Aufbau +leider +verpflichtet +Auffassung +komme +Karriere +bezahlen +Telefon +Rest +Umwelt +schlägt +bewußt +wichtiger +Sonne +trat +gelangen +schneller +Kandidaten +Brief +folgenden +erscheinen +wirtschaftliche +Nr. +Fahrer +erforderlich +Rennen +Nachfrage +Morgen +Verdacht +eigener +Hinweis +Umgebung +bestätigte +Regelung +Fenster +dienen +modernen +zuständig +Flughafen +Generation +gelegt +ergeben +nationalen +Waffen +Schicksal +Entscheidungen +Jugend +Niederlage +Spaß +Niveau +Gelände +Trend +heißen +Tatsache +Presse +Feuer +bezahlt +Förderung +Bildung +vorstellen +grundsätzlich +voller +Winter +italienischen +Botschaft +Investoren +Grad +Geschäfte +Leser +spielte +bayerischen +erzielt +reichen +Einrichtung +Energie +Gelegenheit +regelmäßig +übertragen +Freude +verändert +Umgang +Fans +entweder +Dauer +Finanzierung +Hersteller +Größe +eingestellt +Kontakt +früh +ursprünglich +Prinzip +Ausdruck +definieren +Mittelpunkt +Wunder +eindeutig +Quadratmeter +Mitarbeitern +Behandlung +größeren +Nachbarn +Schriftsteller +Himmel +hoffen +unterstützen +Auge +Risiko +allgemeinen +verdient +Tor +Finanzminister +behauptet +Rücktritt +schwarzen +bewegen +plus +mögliche +passiert +Angesichts +betroffen +durchgeführt +entsteht +Vorwürfe +näher +bekam +Aussage +Verfassung +Bewohner +vorhanden +richtige +übrigens +Kommunen +Städten +verfügt +Filme +Tabelle +historischen +Werbung +fragt +Gegenteil +Fischer +Gegensatz +Bauern +Arzt +Leitung +voraussichtlich +besondere +Abgeordneten +sehe +Arbeiter +Beschluß +festgenommen +Ermittlungen +Minute +Anzahl +Willen +Ruf +aktiv +einiger +westlichen +Schweden +technischen +verwenden +Vorwurf +zwanzig +höheren +offiziell +tief +in der Regel +Du +Ausbau +Auswahl +Voraussetzung +Wissenschaft +richtigen +schien +festgelegt +Bilanz +erhöhen +kurzem +Gang +vorerst +Hände +wirken +Vertrauen +denkt +lösen +Positionen +zählen +Veränderungen +besitzt +Alternative +Falle +Entwurf +Ziele +sichern +ernst +positiv +Tat +ermöglicht +Ideen +ausgeschlossen +Änderung +Ebene +äußerst +Lager +zweimal +Brandenburg +Tür +dringend +Regisseur +Wechsel +Begründung +alter +Wirklichkeit +fehlen +links +Summe +Aktivitäten +Erinnerung +Zugang +praktisch +geöffnet +Leipzig +Frank +Strecke +japanischen +fährt +Wende +Herz +Autoren +fürs +übrigen +Ausgabe +Fußball +kritisierte +Projekte +versteht +Stil +letztlich +Aussagen +Oberbürgermeister +herum +Bundeswehr +Verständnis +steigt +kämpfen +zugunsten +glaube +Fraktion +Posten +Dorf +Post +unmittelbar +Bruder +außen +welcher +Rang +Krankheit +eingerichtet +Auswirkungen +Strom +bloß +Auseinandersetzung +zumal +Struktur +Linie +Felder +Ministerpräsidenten +dient +wußte +falls +entstand +Herrn +Autofahrer +Information +Erwartungen +Sitz +definiert +warf +Fusion +Untersuchungen +gaben +verlor +los +daraufhin +Zinsen +scheinen +Sowjetunion +Büro +angenommen +Ursache +Steuern +fühlen +Anlagen +Franzosen +allzu +liege +verteilt +soviel +Landtag +Nürnberg +ab +Bereichen +stammt +Service +künftigen +Kino +Behörde +gehandelt +Nutzung +ausgerechnet +Interview +Wissen +zuerst +aufgefordert +Gewerkschaft +davor +Ausnahme +Erhöhung +wächst +gearbeitet +Rechte +eins +unterschiedlichen +geradezu +wirft +international +Zug +erreichte +Bedarf +erlebt +teuer +Kritiker +Flucht +Klage +Änderungen +informiert +geschickt +erinnern +Männern +Unternehmer +klingt +Hinweise +kräftig +fort +rein +eigenes +spät +Eröffnung +Beratung +Gefängnis +städtischen +angezeigt +rasch +vorgestellt +erfüllen +freie +fertig +Haushalt +berücksichtigt +schlagen +Direktor +Realität +Falls +Schreiben +Sender +Provinz +Job +festgestellt +Standort +Beruf +gestorben +voraus +gesucht +überwiegend +Stellung +Einigung +Vorschläge +Texte +fühlt +Schuld +leichter +vierten +Halbjahr +Rücken +Innenminister +Karten +halben +betrachtet +schlug +geschehen +Zeitraum +vermeiden +stattfinden +prüfen +unterdessen +ausländischen +vieler +Formen +Tore +starb \ No newline at end of file diff --git a/lang/en/settings.php b/lang/en/settings.php index 388f996..022458c 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -7,4 +7,7 @@ $lang['geoiplib'] = 'Add GeoIP Information
(requires PHP module to be installed)'; $lang['geoiplib_o_disabled'] = 'Disabled'; - $lang['geoiplib_o_phpgeoip'] = 'Use GeoIP Module'; \ No newline at end of file + $lang['geoiplib_o_phpgeoip'] = 'Use GeoIP Module'; + +$lang['useCaptcha'] = 'Enable Captcha
(Experimental, read the manual first!)'; +$lang['captchaSeed'] = 'Captcha Seed
(Enter a 16 to 32 digits random number)'; diff --git a/lang/en/wordlist.txt b/lang/en/wordlist.txt new file mode 100644 index 0000000..cd0e0b1 --- /dev/null +++ b/lang/en/wordlist.txt @@ -0,0 +1,2000 @@ +the +of +and +to +in +a +is +isn’t +was +that +for +as +with +by +on +are +from +be +or +his +were +it +an +at +not +which +have +haven’t +he +had +hadn’t +this +has +also +their +but +one +can +can’t +its +on the +other +been +more +they +used +first +all +two +citation +than +into +would +only +time +who +most +may +such +some +many +when +after +between +over +these +her +about +there +use +no +them +new +him +will +out +during +made +both +then +often +so +any +being +such as +where +number +could +main +through +system +people +known +each +while +if +called +convert +same +later +three +because +well +work +before +the same +under +part +very +different +became +year +did +didn’t +large +example +several +city +early +until +much +government +found +own +since +she +even +form +power +do +those +around +state +including +set +high +life +against +second +century +within +world +still +end +using +small +name +what +now +usually +American +without +however +began +like +as well +area +make +common +the most +water +United States +another +way +due +must +long +less +four +death +said +film +order +due to +back +public +does +left +based +few +become +known as +given +country +major +British +place +group +considered +among +game +point +used to +period +support +war +music +down +million +important +systems +control +should +took +day +family +language +last +original +result +political +line +members +case +as well as +see +single +just +process +along +similar +take +following +we +although +countries +right +either +times +areas +published +the other +local +include +population +never +data +home +every +various +the time +modern +further +development +per +how +led +possible +military +popular +term +though +history +generally +you +off +rather +men +law +developed +German +held +human +production +body +general +the world +light +sometimes +states +late +field +based on +having +came +above +available +book +others +York +next +created +U.S. +show +himself +out of +wrote +days +died +word +play +again +great +service +age +seen +children +level +released +works +continued +pp. +the two +five +higher +species +energy +required +change +means +team +January +information +theory +New York +produced +making +built +design +role +addition +included +almost +side +position +groups +able +de +land +total +range +July +national +space +written +social +version +Europe +season +force +air +allowed +largest +good +type +itself +received +women +low +throughout +taken +standard +little +least +that is +free +cases +size +thus +school +especially +old +upon +particular +terms +effect +provide +lower +certain +together +present +always +short +parts +words +third +April +described +too +up to +established +might +played +forces +natural +June +once +months +rate +European +numbers +six +man +rather than +hand +typically +value +England +London +October +could be +final +the country +September +average +France +instead +current +December +international +program +character +increased +a few +surface +across +thought +company +followed +best +provided +economic +games +significant +named +function +building +went +return +uses +fact +study +below +full +source +lost +America +person +a number +changes +longer +research +individual +languages +strong +structure +party +larger +run +open +cause +aircraft +away +far +region +need +food +forms +increase +outside +started +material +cannot +November +half +head +market +near +record +traditional +special +style +all the +sent +story +February +player +designed +top +at least +themselves +model +returned +band +because of +help +types +come +points +added +events +network +limited +services +nature +army +former +father +close +view +allow +won +specific +elements +practice +lines +gave +pressure +introduced +produce +moved +whether +religious +put +official +movement +my +Germany +students +trade +method +attack +in order +problems +art +eventually +evidence +referred +results +caused +remained +influence +success +meaning +brought +believed +enough +features +give +young +white +culture +problem +characters +lead +action +according +referred to +conditions +performance +according to +effects +amount +black +business +working +better +difficult +temperature +education +real +money +smaller +create +located +directly +sound +leading +ground +therefore +get +private +formed +particularly +Chinese +television +subject +south +cities +album +class +industry +computer +beginning +community +already +complete +go +players +associated +related +physical +our +town +move +complex +interest +rule +speed +commonly +in order to +rights +living +for example +greater +writing +find +growth +killed +court +levels +house +against the +office +done +shows +Spanish +needed +saw +central +entire +fire +son +books +mostly +access +soon +today +earlier +variety +additional +percent +foreign +ever +recorded +future +widely +title +all of +shown +successful +radio +project +construction +changed +king +remains +mass +personal +policy +code +includes +involved +Africa +idea +names +separate +base +length +rules +key +units +likely +makes +release +cost +church +films +majority +primary +performed +methods +stated +appear +whole +so that +member +allows +decided +Japanese +reported +sold +capital +science +society +rest +stage +event +whose +recent +color +legal +highly +career +song +frequently +placed +simple +defined +me +mother +appeared +India +schools +turn +simply +relationship +multiple +nearly +sense +past +relatively +forced +software +attempt +north +William +quickly +companies +programs +products +direct +site +sources +previous +technology +battle +front +provides +necessary +served +originally +along with +approach +image +seven +experience +Greek +parts of +text +towards +completed +memory +ability +commercial +UK +health +replaced +worked +yet +start +economy +highest +property +behind +knowledge +test +response +largely +sea +reached +president +wide +night +operations +supported +act +training +reason +Britain +list +issues +occur +loss +the government +reduced +active +functions +remain +lack +to do +clear +gas +island +cells +taking +contains +takes +approximately +exist +basis +distance +independent +intended +elected +product +adopted +actually +wife +ended +born +Christian +keep +potential +course +matter +love +issue +objects +things +heavy +oil +center +regular +George +the following +divided +eight +date +James +summer +direction +laws +numerous +account +income +individuals +concept +plan +announced +Russian +failed +applied +values +northern +proposed +ships +resulting +estimated +appears +continue +Canada +red +married +object +studies +media +engine +passed +Henry +Australia +machine +quality +founded +hold +say +parties +letter +Japan +expected +treatment +normal +signal +blood +financial +status +video +older +carried +compared +know +wanted +accepted +via +read +each other +types of +animals +turned +unit +feature +southern +increasing +met +prevent +songs +applications +require +opened +marriage +contain +equipment +playing +section +remaining +properties +asked +the case +ten +Charles +activity +whom +ways +buildings +basic +probably +running +materials +stories +edition +location +noted +cell +analysis +Russia +Rome +removed +becomes +paper +activities +billion +fall +child +cut +operating +degree +ship +occurs +less than +novel +completely +except +inside +Robert +troops +Jewish +report +call +flow +metal +appointed +offered +told +frequency +initial +campaign +discovered +definition +historical +models +allowing +operation +police +poor +weight +not have +ancient +here +primarily +agreed +hit +west +heat +security +David +claimed +positive +easily +environment +scientific +going +regions +ISBN +ball +tradition +reach +requires +St. +extended +charge +female +face +serve +equal +Italy +letters +refused +immediately +believe +supply +effective +internal +versions +cultural +leave +th +mainly +negative +soldiers +question +spread +suggested +Paul +difference +unique +fully +musical +relations +civil +cover +something +board +becoming +famous +hard +plants +situation +workers +Indian +shot +medical +instead of +risk +device +decision +techniques +weeks +previously +western +phase +responsible +purpose +race +article +gives +reference +giving +authority +critical +sets +Italian +in the world +ideas +California +growing +element +instance +kept +moving +rock +share +nuclear +causes +reduce +vote +efforts +table +output +car +determined +win +era +notes +finally +ordered +application +develop +distribution +represented +leaving +quite +the population +woman +devices +contrast +offer +tried +presence +the top +mean +scale +us +river +collection +content +attempt to +says +powerful +comes +the best +humans +travel +resulted +proved +African +combined +road +felt +page +receive +tax +address +teams +connected +pay +annual +despite +disease +birth +closed +kind +agreement +actual +price +claim +goal +management +flight +rise +fuel +brother +maximum +month +presented +write +east +male +gold +reaction +powers +resources +latter +plant +middle +motion +observed +rates +your +currently +station +appearance +volume +avoid +consists +deal +families +argued +room +scene +user +depending +advantage +weapons +target +prior +records +temperatures +speech +spent +focus +mission +dead +constant +existence +lived +daughter +signed +relative +blue +occurred +helped +damage +meant +actions +professional +friends +contact +places +solution +Asia +beyond +meeting +structures +standards +care +exchange +peace +sequence +carry +county +nor +organization +chemical +showed +factors +federal +transport +capacity +Washington +e +in that +meet +be used to +claims +upper +pass +perform +attacks +extremely +effort +the public +opening +tend +initially +literature +alone +attention +paid +sides +useful +centuries +fixed +follow +winter +Thomas +crew +origin +serious +alternative +differences +featured +responsible for +centre +pieces +strength +measure +examples +track +plans +officers +shape +star +Richard +joined +refer +command +existing +cards +leaders +figure +plays +Louis +piece +houses +classes +conflict +fields +stars +th century +composed +positions +vary +round +gain +tend to +seeAlso +impact +slightly +behavior +depending on +look +as to +nothing +raised +hands +compared to +reasons +toward +choice +heart +identified +creating +maintain +refers +condition +constructed +overall +reading +marked +fourth +division +failure +saying +tour +recently +formal +unable +have to +needs +mind +stations +digital +religion +Spain +think +grew +represent +typical +parents +display +competition +acid +and so +fell +week +determine +protection +wave +users +build +processes +projects +return to +technique +to keep +fish +cast +drive +starting +unable to +expanded +affected +entirely +combination +forward +resistance +entered +factor +equivalent +attempts +episode +attempted +away from +providing +sector +external +increases +university +ice +ones +achieved +decades +expressed +Americans +double +demand +regarded +refer to +architecture +audience +issued +fight +citizens +match +in all +aid +limit +sexual +goods +seems +costs +distinct +granted +perhaps +improved +green +file +need to +on to +importance +electric +significantly +DNA +cycle +normally +components +describes +creation +TV +layer +tree +bring +fighting +stop +and also +core +format +increasingly +thousands +animal +leader +understanding +break +unknown +classical +extensive +sign +Israel +the people +host +causing +destroyed +protect +describe +Peter +arrived +square +voice +industrial +purposes +magnetic +containing +finished +follows +nation +rare +cars +continues +that time +planned +radiation +recording +advanced +engines +principle +student +islands +philosophy +sought +officials +rapidly +recognized +secondary +particles +pattern +staff +covered +runs +traffic +awarded +lives +deep +the whole +United Kingdom +expansion +identity +context +controlled +images +organizations +derived +Ireland +administration +bodies +opposed +friend +chosen +greatly +and not +designs +nations +surrounding +urban +zero +otherwise +error +native +setting +gained +why +territory +acts +artists +communication +launched +J. +regional +card +freedom +coast +visible +begin +contract +arms +showing +the past +message +generation +involved in +Mary +nine +leaves +stone +ran +global +popularity +broadcast +minor +sites +instruments +improve +wall +contemporary +soil +Edward +carbon +conducted +symbol +as one +at the end +operate +somewhat +be seen +close to +news +communities +developing +influenced +domestic +port +machines +figures +views +got +declared +note +search +contained +employed +input +step +lead to +bridge +respect +worldwide +caused by +the idea +captured +want +maintained +route +achieve +publication +consider +magazine +orders +winning +formation +Australian +ring +reports +onto +subsequent +details +safety +storage +plane +climate +World War II +accept +connection +the field +dark +managed +practical +shared +be found +measures +visit +capable +movie +spring +calls +independence +obtained +aspects +dance +learning +opposition +begins +towns +split +brain +interest in +Jews +at all +technical +directed +introduction +belief +border +owned +prior to +Chicago +doing +involves +suffered +transfer +offers +enemy +correct +possibly +oxygen +greatest +exists +measured +closely +policies +dedicated +press +producing +scholars +steel +heard +accounts +usage +computers +daily +walls +Canadian +fiction +rejected +stable +score +engineering +ensure +indicate +mentioned +eastern +forms of +path +skin +proper +trial +oldest +statement +mixed +most important +officially +portion +reality +the book +dry +heavily +height +authors +fast +hundred +specifically +author +kind of +block +channel +string +Mexico +Jesus +institutions +instrument +residents +combat +selected +vehicles +coming +manner +rarely +Irish +goes +hot +pair +royal +neither +organized +effectively +expensive +really +suggests +processing +opposite +severe +operated +depends +characteristics +regarding +yards +enter +interests +glass +college +a high +practices +testing +bands +the story +trees +ends +facilities +place in +faith +grow +slow +armed +churches +mode +background +job +writers +defeated +cable +leadership +transmission +patterns +thing +Dutch +density +attached +big +bottom +atmosphere +networks +degrees +dropped +electronic +i +might be +strongly +items +roles +translation +pages +apply +assigned +signals +extent +viewed +Michael +debate +minimum +periods +programming +visited +ultimately +frame +hardware +prices +whereas +as a result +establish +permanent +holds +strategy +escape +authorities +acting +join +listed +sex +edge +screen +decade +critics +cold +continuous +generated +solid +violence +liquid +iron +investment +likely to +sufficient +composition +broke +experienced +et +exactly +map +ratio +documents +anything +questions +twice +North America +converted +elections +slaves +threat +time in +studio +contributed +household +wind +train +truth +changing +for each +rural +capable of +sales +secret +bus +football +movements +theories +labor +modified +Christ +cross +expression +stock +atoms +chain +earth +leads +understand +box +impossible +principles +requirements +weather +considerable +director +earliest +vehicle +added to +moral +Johnson +district +literary +respectively +am +set up +prepared +subsequently +preferred +trying +sports +mechanism +concerned +courts +leading to +task +treated +voltage +translated +hydrogen +inspired +victory +advance +appropriate +faster +nearby +theme +to go +drawn +standing +arts +parallel +adding +component +relationships +waves +focused +kill +locations +notable +printed +separated +resolution +tools +officer +subject to +the amount +essential +medium +opportunity +solar +etc. +teaching +argument +don’t +Columbia +GDP +cultures +painting +tests +bit +amounts +molecules +roads +launch +crime +fleet +holding +learned +stay +stored +pure +balance +and all +successfully +picture +politics +circuit +request +Joseph +environmental +distributed +produces +affect +prime +revealed +eye +universe +fans +patients +wood +governments +Berlin +occasionally +selection +taught +acquired +fundamental +linear +Alexander +choose +episodes +floor +angle +incorporated +try +benefit +description +die +studied +Poland +dates +list of +artist +flat +protein +concluded +hundreds +speak +extra +subjects +approved +electrons +chance +easy +implemented +husband +ideal +planning +concepts +grown +understood +extreme +intelligence +quantum +receiving +Scotland +online +knew +unless +Jones +out to +rapid +shall +Texas +academic +reputation +the future +experiments +explained +park +eyes +sections +audio +in fact +persons +store +budget +corresponding +sister +charged +broken +capture +script +rich \ No newline at end of file diff --git a/lang/fr/wordlist.txt b/lang/fr/wordlist.txt new file mode 100644 index 0000000..11da389 --- /dev/null +++ b/lang/fr/wordlist.txt @@ -0,0 +1,1972 @@ +de +je +est +pas +le +vous +la +tu +que +un +il +et +à +a +ne +les +ce +en +on +ça +une +ai +pour +des +moi +qui +nous +mais +y +me +dans +du +bien +elle +si +tout +plus +non +mon +suis +te +au +avec +oui +va +toi +fait +ils +as +être +faire +se +comme +était +sur +quoi +ici +sais +lui +veux +ma +là +rien +dit +es +où +votre +pourquoi +sont +cette +quand +par +son +ton +peux +alors +dire +vais +comment +avez +bon +ou +très +même +merci +ont +jamais +aussi +chose +voir +allez +tous +ces +deux +sa +faut +été +êtes +ta +avoir +fais +peut +autre +maintenant +encore +peu +vraiment +m +mes +temps +toujours +notre +vie +oh +juste +sans +avait +quelque +monde +accord +vu +fois +aller +trop +viens +crois +dois +père +dieu +homme +sûr +aux +leur +avant +étais +besoin +femme +personne +avais +aime +chez +vrai +ans +ses +mal +parler +vos +après +mort +ca +eu +veut +parce que +sera +mieux +bonne +petit +tes +dis +beaucoup +monsieur +voilà +depuis +doit +mère +quel +vas +vois +fille +déjà +gens +donc +jour +il +autres +soir +toute +ouais +argent +maison +nom +bonjour +pense +nos +cela +nuit +avons +ii +merde +cet +papa +maman +reste +peur +désolé +salut +seul +arrive +vite +prendre +regarde +soit +air +quelle +passé +trois +savoir +plaît +choses +fils +ah +bas +moins +entre +passe +hé +demain +appelle +grand +tête +voulez +faites +arrête +hein +attends +ok +raison +enfants +assez +aurais +elles +voulais +sommes +jours +parle +moment +amour +toutes +heure +puis +tard +tuer +eh +dû +ami +petite +partir +hommes +connais +aider +savez +gars +chance +combien +tant +sait +part +voiture +pris +problème +aujourd’hui +coup +porte +serait +prends +venir +travail +pu +famille +seule +sens +allons +putain +idée +ni +contre +revoir +entendu +comprends +passer +pendant +trouvé +trouver +quelques +vient +vieux +aurait +attention +demande +chercher +sous +pouvez +voici +pourrait +sang +histoire +amis +sortir +question +venez +rester +frère +ville +fini +nouveau +eux +truc +tiens +yeux +mois +laisse +mec +longtemps +belle +police +seulement +importe +heures +eau +car +super +chaque +cas +vont +tué +terre +place +main +ensemble +type +beau +pardon +vers +aucun +guerre +trouve +partie +suite +prie +devant +compris +arrivé +mme +leurs +étaient +mettre +matin +aide +dessus +sois +genre +fin +perdu +jeune +chérie +premier +attendez +enfant +donne +venu +aimerais +droit +côté +chambre +loin +donner +devrais +laisser +feu +jouer +ie +train +gros +compte +s +savais +mourir +pouvoir +regardez +parlé +donné +première +aura +dernière +minutes +aucune +mari +enfin +madame +façon +devrait +mis +film +femmes +fort +pourrais +écoute +pays +parti +affaire +endroit +corps +ia +fou +vivre +prêt +dont +espère +grande +cause +point +filles +dehors +hier +boulot +pensais +garçon +près +désolée +cinq +chef +ainsi +haut +celui +demandé +dirait +bébé +possible +école +plein +mains +années +dites +nouvelle +manger +docteur +tour +croire +ceux +quatre +plutôt +marche +semaine +vérité +envie +capitaine +arrêter +affaires +bientôt +vue +demander +dernier +instant +essaie +arriver +font +tellement +derrière +tomber +presque +voulait +meilleur +numéro +journée +appeler +dollars +attendre +confiance +garde +souviens +dur +serai +bureau +voyez +abord +important +devez +ben +peine +cours +fera +prend +seigneur +suffit +route +cul +ils +minute +bonsoir +jeu +croyais +ferme +plaisir +voudrais +heureux +mot +musique +chien +messieurs +prenez +calme +parents +dedans +fous +arrêtez +mariage +entrer +rentrer +ait +lit +voit +autant +reviens +parfait +coeur +ceci +service +téléphone +pauvre +mlle +attend +drôle +ira +parfois +retour +á +verre +pensé +six +ci +impossible +ferai +aimes +payer +facile +maître +new +appelé +mauvais +général +doute +prison +adore +faute +entends +oublié +bras +exactement +fête +café +chéri +sors +gentil +penser +vaut +john +lieu +malade +changer +roi +commence +entendre +président +travailler +partout +cher +morts +rendre +écrit +équipe +joue +sinon +esprit +regarder +rentre +plan +cœur +veulent +montrer +voulu +boire +travaille +propre +année +état +soyez +bois +cherche +laissé +essayer +dès +semble +ies +jack +dix +faisait +génial +penses +sécurité +tôt +mets +rêve +armée +perdre +parles +avis +surtout +difficile +york +dormir +ensuite +pire +ecoute +simple +allait +paix +morte +arme +sujet +retard +voyons +livre +appris +peuvent +sale +serais +souvent +an +sauf +choix +sûrement +étiez +or +visage +ordre +comprendre +essayé +noir +dîner +âge +chemin +changé +bout +face +rue +inquiète +photo +allé +personnes +sérieux +ciel +honneur +amie +questions +force +garder +petits +tirer +millions +grave +marché +nouvelles +voix +semaines +pouvais +courant +propos +bateau +oublie +con +celle +gauche +content +prix +rouge +faim +ferais +avion +devenir +devoir +prochaine +restez +acheter +voyage +sorte +long +espèce +idiot +gueule +début +bouge +continue +hôpital +sort +grâce +problèmes +message +certains +patron boss +sûre +reçu +trucs +avaient +ouvre +promis +the +oncle +euh +connaissez +laissez +devons +bienvenue +occupe +allais +camp +manque +soleil +devait +pars +pouvait +cheveux +armes +pensez +salle +croyez +bizarre +gagner +commencé +fond +sauver +pièce +erreur +ailleurs +rapport +froid +apprendre +scène +secret +sac +seconde +cru +allô +revenir +battre +hôtel +soirée +aurai +soeur +pieds +seras +carte +jolie +n +pied +groupe +venue +monte +agent +fallait +effet +libre +seront +foutre +bordel +mots +neuf +tire +faux +situation +tue +lumière +vieille +droite +debout +noël +cheval +intérieur +écoutez +joli +gagné +loi +entrez +auras +incroyable +lettre +présent +occuper +connaît +absolument +aviez +dame +professeur +fric +george +retrouver +coin +table +colonel +âme +dos +magnifique +rencontrer +réussi +meilleure +rappelle +tranquille +fut +chaud +commencer +sœur +agit +cool +doucement +pareil +jeunes +joe +accident +appel +anniversaire +blanc -white +finir +risqué +doivent +moyen +terminé +complètement +clair +meurtre +sam +parlez +heureuse +ordres +disais +touche +frank +prêts +fasse +puisse +déjeuner +envoyé +lire +disait +tombe +su +marcher +avance +déteste +forme +â +mauvaise +bord +décidé +mer +vivant +médecin +midi +devenu +montre +porter +vraie +disent +ferait +ignore +charlie +connaître +silence +diable +sortez +tom +cadeau +suppose +flics +avocat +jure +anglais +sept +devriez +moitié +aimé +surprise +chacun +serez +exact +commandant +télé +autour +disparu +ligne +expliquer +aimer +arrivée +grosse +simplement +mission +monter +tenir +bons +balle +quitter +selon +classe +paris +sorti +mange +peuple +habitude +voie +dangereux +pote +contrôle +prête +honte +rencontré +photos +impression +folle +écrire +suivre +oublier +livres +aille +retourner +offre +chanson +envoie +trou +arrière +poste +amérique +huit +radio +paul +ressemble +attaque +arrêté +change +baiser +pourtant +réponse +connu +connard +lu +pute +bande +enfer +tenez +rend +triste +tel +bravo +être +savait +david +plusieurs +existe +met +rire +compagnie +étrange +exemple +devais +vit +combat +secours +aurez +conneries +visite +joué +michael +pourra +coucher +imagine +bob +merveilleux +continuer +écoutez +dirai +volé +lune +bouche +sud +danse +ennuis +but +aie +hors +sortie +boîte +vol +public +lieutenant +écouter +présente +système +époque +retourne +certain +bête +vendre +avenir +grands +coups +santé +partez +amoureux +envoyer +cuisine +normal +danger +gouvernement +village +poser +ouvrir +journal +approche +faudra +dommage +peau +pleine +voler +nez +danser +servir +sympa +bonnes +mille +héros +banque +sergent +clé +secondes +nord +inspecteur +hey +liberté +salaud +plait +cour +juge +viennent +bruit +flic +tient +terrible +paraît +crime +prochain +pouvons +assis +thé +bonheur +payé +tas +travers +stupide +blague +préfère +conseil +robe +tiré +protéger +rêves +seuls +pitié +oeil +mêmes +vin +avions +don +sol +vent +club +garçons +gamin +tante +croit +mesdames +bar +milieu +reine +signe +centre +probablement +bière +dingue +inutile +nulle +mike +sent +différent +emmener +comprenez +verra +prise +vêtements +liste +savent +unis +société +chère +soin +pierre +appelez +utiliser +acheté +laquelle +fiche +parole +mecs +faisais +marier +départ +ennemi +irai +aimez +spectacle +vouloir +recherche +devient +choisi +you +pose +films +intérêt +intéresse +rôle +johnny +félicitations +descendre +harry +tourne +position +sert +blessé +humain +asseoir +match +coupable +environ +art +mise +londres +espoir +quitté +lequel +mur +peter +ouvert +église +salope +beauté +lâche +adresse +mademoiselle +petites +sauter +colère +directeur +adieu +jimmy +faisons +parie +entrée +tort wrong +conduire +américain +revenu +justice +soldat +expérience +auriez +cerveau +fenêtre +quartier +prince +vis +promets +venus +soldats +tombé +riche +fleurs +bill +mary +œil +présenter +presse +frères +ridicule +preuve +ouvrez +revient +pourriez +épouser +intéressant +gosse +faudrait +histoires +rose +veuillez +nature +vide +responsable +courage +capable +cinéma +max +décision +taxi +chanter +excuse +sentir +jim +portes +james +fier +deuxième +eiie +appartement +contact +essayez +sauvé +cacher +ià +répondre +manière +aimais +jambes +occasion +défense +longue +jésus +jeter +allée +formidable +base +telle +san +glace +dents +parmi +immédiatement +marié +paie +nul +machine +vacances +épouse +monstre +souvenir +tueur +né +continuez +français +rendu +course +majesté +mien +types +certainement +coupe +importance +ouest +idées +gagne +oubliez +chat +ayez +trouves +dégage +chier +ravi +étions +arrivent +joie +prévu +lorsque +superbe +touché +bain +contente +horrible +retrouve +court +dérange +and +bombe +ramener +certaines +réalité +enquête +camion +foutu +signifie +cent +toucher +occupé +attendais +rencontre +excellent +respect +quitte +tourner +terrain +blanche +projet +éviter +poisson +plupart +descends +joyeux +empêcher +mariée +mangé +envers +km +ange +meilleurs +copine +chaussures +dossier +règles +dure +animaux +langue +américains +imbécile +chevaux +princesse +zone +pius +drogue +charge +enfoiré +nourriture +pont +refuse +chiens +contraire +enchanté +douleur +politique +faits +arrivera +essaye +magasin +soient +dirais +entier +vécu +bus +offrir +chapeau +raconte +cartes +henry +discuter +papier +action +permis +bu +rapide +shérif +frais +partis +emmène takes +réunion +mettez +île +toilettes +code +billy +opération +spécial +planète +viendra +champ +couleur +pain +excuser +destin +rends +découvert +puisque +taille +nick +tony +vaisseau +sexe +sacré +repas +contrat +qu +nécessaire +client +détruire +lait +faite +mémoire +pleurer +copain +miss +restes +ray +personnel +double +mignon +couteau +sache +témoin +foi +dors +remercie +direction +steve +malin +niveau +remettre +habite +apporté +aiment +mienne +procès +doux +eddie +trouvez +solution +raconter +fermer +choisir +goût +nouveaux +end +amuser +différence +stop +charles +gosses +réfléchir +clients +enceinte +beaux +université +bleu +mètres +vus +aimait +marre +menti +appelles +courir +arranger +travaillé +papiers +retrouvé +officier +espace +sourire +dise +mérite +lettres +accepter +quels +failli +angleterre +vitesse +jambe +caméra +rappeler +arbre +pièces +forces +trésor +victime +énergie +disons +énorme +rentré +alex +appartient +préparer +propres +regrette +finalement +france +invité +moindre +censé +washington +bobby +image +justement +compter +enlever +chasse +unique +fout +arrêt +file +balles +preuves +dimanche +lycée +fil +morceau +noms +durant +sarah +cassé +doigts +vérifier +million +appareil +tirez +apporte +réponds +piste +derniers +troisième +dépend +humains +programme +honnête +voitures +bougez +richard +belles +régler +flingue +couper +noire +entendez +aveugle +bouger +venait +présence +savons +crise +amène +interdit +trés +lever +partons +clés +obtenir +pluie +récupérer +grandes +quelles +prouver +souris +restaurant +regard +okay +intention +forte +danny +cou +points +manqué +proche +urgence +folie +ancien +relation +bouteille +étage +rejoindre +casse +jardin +malgré +oiseau +méchant +raisons +perds +pleure +meurt +claire +frappé +chante +pourrai +nombre +robert +gentille +pourras +sentiments +regardé +toit +lors +métier +maladie +poche +três +frapper +tommy +succès +étant +lee +dessous +voudrait +théâtre +chinois +crains +afin +gare +billets +douce +frappe +remarqué +paradis +étranger +campagne +fermé +alcool +jerry +vendu +samedi +parlait +produit +répète +amoureuse +souhaite +odeur +appelait +vôtre +attendant +montagne +oû +vies +demandez +fantastique +victoire +mens +carrière +enlève +fatigué +suivi +animal +raté +règle +meurs +remercier +assurer +queue +viande +rivière +falloir +suivant +assure +obligé +martin +fusil +passage +hasard +pauvres +neige +parfaitement +journaux +échapper +plage +secrets +signal +crétin +aies +vidéo +coffre +pression +costume +informations +futur +disant +univers +auraient +gardes +lis +volonté +attendu +verras +donnez +demandais +excuses +vienne +pensent +faible +cache +fiston +van +prendra +attraper +dort +décider +inquiétez +cesse +gaffe +serons +ministre +naissance +agir +utile +gaz +bataille +noirs +vouliez +retraite +voleur +poids +discours +nouvel +gâteau +cent +ventre +connaissance +voulons +vive +blancs +assassin +jeux +vendredi +europe +accepte +parlons +certaine +couple +sérieusement +militaire +date +rome +entend +droits +resté +montez +titre +génie +couilles +revenez +recevoir +autrement +répondu +amené +os +perd +valeur +puissant +style +vert +fortune +vieil +major +amener +prépare +poulet +embrasser +genoux +acte +détruit +célèbre +cible +cousin +conscience +prévenir +ramène +arrivés +doigt +californie +article +cherchez +étoiles +milliers +reprendre +mine +regardes +servi +ennemis +charmant +sentiment +reposer +note +couché +gardez +jake +agréable +anna +préféré +château +bal +oreilles +découvrir +apparemment +l’os +zéro +saint +côtés +marrant +comprend +pourrez +joues +salon +lève +utilise +conversation +pilote +fer +attendent +humaine +conduit +gorge +victimes +détails +talent +passé +iui +Marie +privé +apporter +tuée +Le Havre +violence +feras +nerveux +aéroport +direct +enculé +tribunal +commande +jette +paquet +fumer +aise +dieux +emmerde +joindre +lance +usine +défendre +forêt +champion +horreur +importante +paroles +test +désormais +tres +dernières +tombée +Jaques +extérieur +désert +Louis +Angelique +coupé +intelligent +Calais +aide +saura +dira +job +passera +comptes +mode +parlais +invités +série +pis +sage +rappelles +concerne +mariés +grandi +pousse +vache +accepté +meme +pensait +repos +ordinateur +rock +allo +Paris +ombre +russe +minuit +vole +amusant +membres +jane +moyens +surpris +ouverte +nombreux +également +imaginer +mince +dormi +page +essence +maire +faisant +lundi +entré +billet +moteur +réparer +franchement +caché +cigarette +riches +partager +puissance +trouvée +parfaite +hiver +épée +reviendra +haute +souffle +tente +vingt +miracle +arbres +apprécie +senti +manquer +artiste +créer +lac +jeté +souvenirs +sommeil +vrais +réveiller +Lyon +leçon +casser +courses +chemise +fantôme +Fribourg +membre +passez +acteur +battu +parc +Bruxelles +Montreal +services +rues +reculez +japonais +Lourdes +mentir +Marseille +rentrez +plans +Arles +coûte +Champagne +américaine +risques +pète +offert +Orléans +semblant +annonce +Rouen +échange +Lille +premiers +débarrasser +fuir +Supplie +saute +signer \ No newline at end of file From 12993035b5a997893e460942f7a5343107194a53 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Thu, 23 Oct 2025 15:01:36 +0200 Subject: [PATCH 02/42] Captcha implementation Also Dada generator is OK --- action.php | 215 +- captcha.js | 166 ++ conf/metadata.php | 6 +- lang/de/wordlist.txt | 4733 +++++++++++++++++++++---------------- lang/en/wordlist.txt | 5052 ++++++++++++++++++++++++---------------- lang/fr/wordlist.txt | 5305 ++++++++++++++++++++++++++---------------- lang/la/wordlist.txt | 1577 +++++++++++++ style.less | 38 +- 8 files changed, 11097 insertions(+), 5995 deletions(-) create mode 100644 captcha.js create mode 100644 lang/la/wordlist.txt diff --git a/action.php b/action.php index b3536e8..4a9bc68 100644 --- a/action.php +++ b/action.php @@ -62,7 +62,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) ? $INFO['userinfo']['name'] : ''); // build the tracker code: - $code = "document._botmon = {'t0': Date.now(), 'session': '" . json_encode($this->sessionId) . "'};" . NL; + $code = "document._botmon = {t0: Date.now(), session: " . json_encode($this->sessionId) . ", seed: " . json_encode($this->getConf('captchaSeed')) . ", ip: " . json_encode($_SERVER['REMOTE_ADDR']) . "};" . NL; if ($username) { $code .= DOKU_TAB . DOKU_TAB . 'document._botmon.user = "' . $username . '";'. NL; } @@ -74,7 +74,6 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "e.src='".DOKU_BASE."lib/plugins/botmon/client.js';" . NL; $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(e);" . NL; $code .= DOKU_TAB . DOKU_TAB . "});"; - $event->data['script'][] = ['_data' => $code]; } @@ -91,7 +90,6 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $event->data['script'][] = ['src' => DOKU_BASE.'lib/plugins/botmon/admin.js', 'defer' => 'defer', '_data' => '']; } - /** * Writes data to the server log. * @@ -191,39 +189,216 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { public function showCaptcha(Event $event) { - if ($this->getConf('useCaptcha') && $this->checkCaptchaCookie()) { + $useCaptcha = $this->getConf('useCaptcha'); - $event->preventDefault(); // don't show normal content - $this->insertDadaFiller(); // show dada filler instead! - $this->insertCaptchaLoader(); // and load the captcha - - } else { - echo '

Normal page.

'; + if ($useCaptcha !== 'disabled' && $this->checkCaptchaCookie()) { + echo '

'; tpl_pagetitle(); echo "

\n"; // always show the original page title + $event->preventDefault(); // don't show normal content + switch ($useCaptcha) { + case 'blank': + $this->insertBlankBox(); // show dada filler instead of text + break; + case 'dada': + $this->insertDadaFiller(); // show dada filler instead of text + break; + } + $this->insertCaptchaLoader(); // and load the captcha } } private function checkCaptchaCookie() { - $cookieVal = isset($_COOKIE['_c_']) ? $_COOKIE['_c_'] : ''; - $seed = $this->getConf('captchaSeed'); + $cookieVal = isset($_COOKIE['captcha']) ? $_COOKIE['captcha'] : null; - return ($cookieVal == $seed ? 0 : 1); // #TODO: encrypt with other data + $today = new DateTime(); + $isodate = substr((new DateTime())->format('c'), 0, 10); + + $raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $isodate; + + return $cookieVal !== hash('sha256', $raw); } private function insertCaptchaLoader() { + echo '' . NL; + + } + + // inserts a blank box to ensure there is enough space for the captcha: + private function insertBlankBox() { + + echo '

 

'; + } + + /* Generates a few paragraphs of Dada text to show instead of the article content */ + private function insertDadaFiller() { + + global $conf; + global $TOC; + global $ID; + + // list of languages to search for the wordlist + $langs = array_unique([$conf['lang'], 'la']); + + // find path to the first available wordlist: + foreach ($langs as $lang) { + $filename = __DIR__ .'/lang/' . $lang . '/wordlist.txt'; /* language-specific wordlist */ + if (file_exists($filename)) { + break; + } + } + + // load the wordlist file: + if (file_exists($filename)) { + $words = array(); + $totalWeight = 0; + $lines = file($filename, FILE_SKIP_EMPTY_LINES); + foreach ($lines as $line) { + $arr = explode("\t", $line); + $arr[1] = ( count($arr) > 1 ? (int) trim($arr[1]) : 1 ); + $totalWeight += (int) $arr[1]; + array_push($words, $arr); + } + } else { + echo ''; + return; + } + + // If a TOC exists, use it for the headlines: + if(is_array($TOC)) { + $toc = $TOC; + } else { + $meta = p_get_metadata($ID, '', METADATA_RENDER_USING_CACHE); + //$tocok = (isset($meta['internal']['toc']) ? $meta['internal']['toc'] : $tocok = true); + $toc = isset($meta['description']['tableofcontents']) ? $meta['description']['tableofcontents'] : null; + } + if (!$toc) { // no TOC, generate my own: + $hlCount = mt_rand(0, (int) $conf['tocminheads']); + $toc = array(); + for ($i=0; $i<$hlCount; $i++) { + array_push($toc, $this->dadaMakeHeadline($words, $totalWeight)); // $toc + } + } + + // if H1 heading is not in the TOC, add a chappeau section: + $chapeauCount = mt_rand(1, 3); + if ((int) $conf['toptoclevel'] > 1) { + echo "
\n"; + for ($i=0; $i<$chapeauCount; $i++) { + echo $this->dadaMakeParagraph($words, $totalWeight); + } + echo "
\n"; + } + + // text sections for each sub-headline: + foreach ($toc as $hl) { + echo $this->dadaMakeSection($words, $totalWeight, $hl); + } + } + + private function dadaMakeSection($words, $totalWeight, $hl) { + + global $conf; + + // how many paragraphs? + $paragraphCount = mt_rand(1, 4); + + // section level + $topTocLevel = (int) $conf['toptoclevel']; + $secLevel = $hl['level'] + 1;; + + // return value: + $sec = ""; + + // make a headline: + if ($topTocLevel > 1 || $secLevel > 1) { + $sec .= "{$hl['title']}\n"; + } + + // add the paragraphs: + $sec .= "
\n"; + for ($i=0; $i<$paragraphCount; $i++) { + $sec .= $this->dadaMakeParagraph($words, $totalWeight); + } + $sec .= "
\n"; + + return $sec; + } + + private function dadaMakeHeadline($words, $totalWeight) { + + // how many words to generate? + $wordCount = mt_rand(2, 5); + + // function returns an array: + $r = Array(); + + // generate the headline: + $hlArr = array(); + for ($i=0; $i<$wordCount; $i++) { + array_push($hlArr, $this->dadaSelectRandomWord($words, $totalWeight)); + } + + $r['title'] = ucfirst(implode(' ', $hlArr)); + + $r['hid'] = preg_replace('/[^\w\d\-]+/i', '_', strtolower($r['title'])); + $r['type'] = 'ul'; // always ul! + $r['level'] = 1; // always level 1 for now + + return $r; + } + + private function dadaMakeParagraph($words, $totalWeight) { + + // how many words to generate? + $sentenceCount = mt_rand(2, 5); + + $paragraph = array(); + for ($i=0; $i<$sentenceCount; $i++) { + array_push($paragraph, $this->dadaMakeSentence($words, $totalWeight)); + } + + return "

\n" . implode(' ', $paragraph) . "\n

\n"; } - private function insertDadaFiller() { - // #TODO: make a dada filler + private function dadaMakeSentence($words, $totalWeight) { + + // how many words to generate? + $wordCount = mt_rand(4, 20); - echo '

'; tpl_pagetitle(); echo "

\n"; - - echo ''; - - echo "

Placeholder text while the captcha is being displayed.

\n"; + // generate the sentence: + $sentence = array(); + for ($i=0; $i<$wordCount; $i++) { + array_push($sentence, $this->dadaSelectRandomWord($words, $totalWeight)); + } + return ucfirst(implode(' ', $sentence)) . '.'; } + private function dadaSelectRandomWord($list, $totalWeight) { + + // get a random selection: + $rand = mt_rand(0, $totalWeight); + + // match the selection to the weighted list: + $cumulativeWeight = 0; + for ($i=0; $i= $rand) { + return $list[$i][0]; + } + } + return '***'; + } + } \ No newline at end of file diff --git a/captcha.js b/captcha.js new file mode 100644 index 0000000..beed194 --- /dev/null +++ b/captcha.js @@ -0,0 +1,166 @@ +"use strict"; +/* DokuWiki BotMon Captcha JavaScript */ +/* 22.10.2025 - 0.1.0 - pre-release */ +/* Author: Sascha Leib */ + +const $BMCaptcha = { + + init: function() { + /* mark the page to contain the captcha styles */ + document.getElementsByTagName('body')[0].classList.add('botmon_captcha'); + + $BMCaptcha.install() + }, + + install: function() { + // find the parent element: + let bm_parent = document.getElementsByTagName('body')[0]; + + // create the dialog: + const dlg = document.createElement('dialog'); + dlg.setAttribute('closedby', 'none'); + dlg.setAttribute('open', 'open'); + dlg.id = 'botmon_captcha_box'; + dlg.innerHTML = '

Captcha box

Checking if you are a human …

'; + + // Checkbox: + const lbl = document.createElement('label'); + const cb = document.createElement('input'); + cb.setAttribute('type', 'checkbox'); + cb.addEventListener('click', $BMCaptcha._cbCallback); + lbl.appendChild(cb); + lbl.appendChild(document.createTextNode('I am a human.')); + + dlg.appendChild(lbl); + + bm_parent.appendChild(dlg); + }, + + /* creates a digest hash for the cookie function */ + digest: { + + /* simple SHA hash function - adapted from https://geraintluff.github.io/sha256/ */ + hash: function(ascii) { + + // shortcut: + const sha256 = $BMCaptcha.digest.hash; + + // helper function + const rightRotate = function(v, a) { + return (v>>>a) | (v<<(32 - a)); + }; + + var mathPow = Math.pow; + var maxWord = mathPow(2, 32); + var lengthProperty = 'length' + var i, j; + var result = '' + + var words = []; + var asciiBitLength = ascii[lengthProperty]*8; + + //* caching results is optional - remove/add slash from front of this line to toggle + // Initial hash value: first 32 bits of the fractional parts of the square roots of the first 8 primes + // (we actually calculate the first 64, but extra values are just ignored) + var hash = sha256.h = sha256.h || []; + // Round constants: first 32 bits of the fractional parts of the cube roots of the first 64 primes + var k = sha256.k = sha256.k || []; + var primeCounter = k[lengthProperty]; + /*/ + var hash = [], k = []; + var primeCounter = 0; + //*/ + + var isComposite = {}; + for (var candidate = 2; primeCounter < 64; candidate++) { + if (!isComposite[candidate]) { + for (i = 0; i < 313; i += candidate) { + isComposite[i] = candidate; + } + hash[primeCounter] = (mathPow(candidate, .5)*maxWord)|0; + k[primeCounter++] = (mathPow(candidate, 1/3)*maxWord)|0; + } + } + + ascii += '\x80' // Append Ƈ' bit (plus zero padding) + while (ascii[lengthProperty]%64 - 56) ascii += '\x00' // More zero padding + for (i = 0; i < ascii[lengthProperty]; i++) { + j = ascii.charCodeAt(i); + if (j>>8) return; // ASCII check: only accept characters in range 0-255 + words[i>>2] |= j << ((3 - i)%4)*8; + } + words[words[lengthProperty]] = ((asciiBitLength/maxWord)|0); + words[words[lengthProperty]] = (asciiBitLength) + + // process each chunk + for (j = 0; j < words[lengthProperty];) { + var w = words.slice(j, j += 16); // The message is expanded into 64 words as part of the iteration + var oldHash = hash; + // This is now the undefinedworking hash", often labelled as variables a...g + // (we have to truncate as well, otherwise extra entries at the end accumulate + hash = hash.slice(0, 8); + + for (i = 0; i < 64; i++) { + var i2 = i + j; + // Expand the message into 64 words + // Used below if + var w15 = w[i - 15], w2 = w[i - 2]; + + // Iterate + var a = hash[0], e = hash[4]; + var temp1 = hash[7] + + (rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25)) // S1 + + ((e&hash[5])^((~e)&hash[6])) // ch + + k[i] + // Expand the message schedule if needed + + (w[i] = (i < 16) ? w[i] : ( + w[i - 16] + + (rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15>>>3)) // s0 + + w[i - 7] + + (rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2>>>10)) // s1 + )|0 + ); + // This is only used once, so *could* be moved below, but it only saves 4 bytes and makes things unreadble + var temp2 = (rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)) // S0 + + ((a&hash[1])^(a&hash[2])^(hash[1]&hash[2])); // maj + + hash = [(temp1 + temp2)|0].concat(hash); // We don't bother trimming off the extra ones, they're harmless as long as we're truncating when we do the slice() + hash[4] = (hash[4] + temp1)|0; + } + + for (i = 0; i < 8; i++) { + hash[i] = (hash[i] + oldHash[i])|0; + } + } + + for (i = 0; i < 8; i++) { + for (j = 3; j + 1; j--) { + var b = (hash[i]>>(j*8))&255; + result += ((b < 16) ? 0 : '') + b.toString(16); + } + } + return result; + } + }, + + _cbCallback: function(e) { + if (e.target.checked) { + //document.getElementById('botmon_captcha_box').close(); + + // make a hash for the cookie: + const seed = document._botmon.seed || ''; + const extIp = document._botmon.ip || '0.0.0.0'; + const d = new Date(document._botmon.t0); + const raw = seed + '|' + location.hostname + '|' + extIp + '|' + d.toISOString().substring(0, 10); + + const hash = $BMCaptcha.digest.hash(raw); + console.log('Setting cookie to:', raw, ' --> ', hash); + document.cookie = "captcha=" + hash + ';'; + + window.location.reload(true); + } + } + +} +// initialise the captcha module: +$BMCaptcha.init(); \ No newline at end of file diff --git a/conf/metadata.php b/conf/metadata.php index 1c02879..32c61a1 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -8,5 +8,7 @@ $meta['geoiplib'] = array('multichoice', '_choices' => array ('disabled', 'phpgeoip')); -$meta['useCaptcha'] = array('onoff'); -$meta['captchaSeed'] = array('string', '_pattern' => '/[\da-fA-F]{16,32}/'); +//$meta['useCaptcha'] = array('onoff'); +$meta['useCaptcha'] = array('multichoice', + '_choices' => array ('disabled', 'blank', 'dada')); +$meta['captchaSeed'] = array('string'); diff --git a/lang/de/wordlist.txt b/lang/de/wordlist.txt index 1b968f2..d756082 100644 --- a/lang/de/wordlist.txt +++ b/lang/de/wordlist.txt @@ -1,2000 +1,2733 @@ -der -die -und -in -den -von -zu -das -mit -sich -des -auf -für -ist -im -dem -nicht -ein -eine -als -auch -es -an -werden -aus -er -hat -daß -sie -nach -wird -bei -einer -um -am -sind -noch -wie -einem -über -einen -so -Sie -zum -war -haben -nur -oder -aber -vor -zur -bis -mehr -durch -man -sein -wurde -sei -Prozent -hatte -kann -gegen -vom -können -schon -wenn -habe -seine -Mark -ihre -dann -unter -wir -soll -ich -eines -Es -Jahr -zwei -Jahren -diese -dieser -wieder -keine -Uhr -seiner -worden -will -zwischen -immer -Millionen -was -sagte -Er -gibt -alle -diesem -seit -muß -wurden -beim -doch -jetzt -waren -drei -Jahre -neue -neuen -damit -bereits -da -ihr -seinen -müssen -ab -ihrer -ohne -sondern -selbst -ersten -nun -etwa -heute -ihren -weil -ihm -seien -Menschen -Deutschland -anderen -werde -sagt -rund -Aber -ihn -Ende -jedoch -Zeit -sollen -ins -Wenn -seinem -uns -Stadt -geht -sehr -hier -ganz -erst -wollen -Berlin -vor allem -sowie -hatten -kein -deutschen -machen -lassen -Als -Unternehmen -andere -ob -dieses -steht -dabei -wegen -weiter -denn -beiden -einmal -etwas -nichts -allerdings -vier -gut -viele -wo -viel -dort -alles -wäre -SPD -kommt -vergangenen -denen -fast -fünf -könnte -hätten -Frau -Am -dafür -kommen -diesen -letzten -zwar -großen -dazu -Mann -sollte -würde -also -bisher -Leben -Milliarden -Welt -Regierung -konnte -ihrem -Frauen -während -Land -zehn -würden -stehen -ja -USA -heißt -dies -zurück -Kinder -dessen -ihnen -deren -sogar -Frage -gewesen -erste -gab -liegt -gar -davon -gestern -geben -Teil -Polizei -dass -hätte -eigenen -kaum -sieht -große -Denn -weitere -sehen -macht -Angaben -weniger -gerade -läßt -Geld -München -deutsche -allen -darauf -wohl -später -könne -deshalb -aller -kam -Arbeit -mich -gegenüber -nächsten -bleibt -wenig -lange -gemacht -Fall -mir -gehen -Berliner -mal -Weg -CDU -wollte -sechs -keinen -Woche -dagegen -alten -möglich -gilt -erklärte -müsse -könnten -Geschichte -zusammen -finden -Tag -Art -erhalten -Man -Dollar -Wochen -jeder -nie -bleiben -besonders -Jahres -Deutschen -Den -Zu -zunächst -derzeit -allein -deutlich -Entwicklung -weiß -einige -sollten -Präsident -geworden -statt -Bonn -Platz -inzwischen -Nur -Freitag -pro -seines -Montag -Europa -schließlich -Sonntag -einfach -gehört -eher -oft -Zahl -neben -hält -weit -Partei -meisten -Thema -zeigt -Politik -Aus -zweiten -Januar -insgesamt -je -mußte -Anfang -hinter -ebenfalls -ging -Mitarbeiter -darüber -vielen -Ziel -darf -Seite -fest -hin -erklärt -Namen -Haus -An -Frankfurt -Gesellschaft -Mittwoch -damals -Dienstag -Hilfe -Mai -Markt -Tage -Donnerstag -halten -gleich -nehmen -solche -Entscheidung -besser -alte -Leute -Ergebnis -Samstag -sagen -System -März -tun -Monaten -kleinen -lang -knapp -bringen -wissen -Kosten -Erfolg -bekannt -findet -daran -künftig -wer -acht -Grünen -schnell -Grund -scheint -Zukunft -bin -liegen -politischen -Gruppe -Rolle -stellt -Juni -sieben -September -nämlich -Männer -Oktober -Mrd. -überhaupt -eigene -gegeben -Stunden -eigentlich -Meter -ließ -Probleme -vielleicht -ebenso -Bereich -zum Beispiel -Höhe -Familie -Bild -Ländern -Informationen -Frankreich -Tagen -schwer -zuvor -genau -April -stellen -neu -erwartet -Hamburg -sicher -führen -Mal -mehrere -Wirtschaft -Mio -Programm -offenbar -Hier -weiteren -natürlich -konnten -stark -Dezember -Juli -ganze -kommenden -Kunden -bekommen -eben -kleine -trotz -wirklich -Lage -Länder -leicht -gekommen -Spiel -laut -November -kurz -politische -führt -innerhalb -meint -immer wieder -Form -Münchner -AG -anders -ihres -völlig -beispielsweise -gute -bislang -August -Hand -jede -GmbH -Film -Minuten -erreicht -beide -Musik -Kritik -Mitte -Verfügung -Buch -dürfen -jeweils -einigen -Umsatz -spielen -Daten -welche -müßten -hieß -paar -nachdem -Kunst -Euro -gebracht -Problem -jeden -Ihre -Sprecher -recht -erneut -längst -europäischen -Sein -Eltern -Beginn -besteht -Seine -mindestens -machte -Jetzt -bietet -außerdem -Bürger -Trainer -bald -Deutsche -Fragen -klar -Seiten -gehören -erstmals -Februar -zeigen -Titel -Stück -größten -FDP -setzt -Wert -Frankfurter -Staat -möchte -daher -wolle -Bundesregierung -lediglich -Nacht -Krieg -Opfer -Tod -nimmt -Firma -zuletzt -Werk -hohen -leben -unter anderem -Kirche -weiterhin -gebe -gestellt -Mitglieder -Rahmen -zweite -Paris -Situation -gefunden -Wochenende -internationalen -Wasser -Recht -sonst -stand -Hälfte -Möglichkeit -versucht -blieb -junge -Mehrheit -Straße -Sache -arbeiten -Monate -Mutter -berichtet -letzte -Gericht -wollten -Ihr -zwölf -zumindest -Wahl -genug -Weise -Vater -Bericht -amerikanischen -hoch -beginnt -Wort -obwohl -Kopf -spielt -Interesse -Westen -verloren -Preis -jedem -erreichen -setzen -spricht -früher -teilte -Landes -zudem -einzelnen -bereit -Blick -Druck -Bayern -Kilometer -gemeinsam -Bedeutung -Chance -Politiker -Zwei -besten -Ansicht -endlich -Stelle -direkt -Bevölkerung -solchen -Alle -solle -jungen -Einsatz -richtig -größte -sofort -neuer -ehemaligen -unserer -dürfte -schaffen -Augen -Rußland -Internet -Raum -Mannschaft -neun -kamen -Ausstellung -Zeiten -Dem -einzige -meine -Nun -Verfahren -Angebot -Richtung -Projekt -niemand -Kampf -weder -tatsächlich -Personen -Heute -geführt -Gespräch -Kreis -Hamburger -Schule -guten -Hauptstadt -durchaus -Zusammenarbeit -darin -Amt -Schritt -meist -groß -zufolge -Sprache -Region -Punkte -Vergleich -genommen -gleichen -du -Ob -Soldaten -Universität -verschiedenen -Kollegen -neues -Bürgermeister -Angst -stellte -Sommer -danach -anderer -gesagt -Sicherheit -Macht -Bau -handelt -Folge -Bilder -lag -Osten -Handel -sprach -Aufgabe -Chef -frei -dennoch -DDR -hohe -Firmen -bzw -Koalition -Mädchen -entwickelt -fand -Diskussion -bringt -Hause -Gefahr -per -zugleich -früheren -dadurch -ganzen -abend -erzählt -Streit -Vergangenheit -Parteien -Verhandlungen -jedenfalls -gesehen -französischen -Trotz -darunter -Spieler -forderte -Beispiel -Meinung -wenigen -Publikum -sowohl -meinte -mag -Auto -Lösung -Boden -Präsidenten -hinaus -verletzt -weltweit -Sohn -bevor -Peter -mußten -keiner -Produktion -Ort -braucht -Zusammenhang -Kind -Verein -sprechen -Aktien -gleichzeitig -London -sogenannten -Richter -geplant -Italien -Mittel -her -freilich -Mensch -großer -Bonner -wenige -öffentlichen -Unterstützung -dritten -nahm -Bundesrepublik -Arbeitsplätze -bedeutet -Feld -Dr. -Bank -oben -gesetzt -Ausland -Ministerpräsident -Vertreter -z.B. -jedes -ziehen -Parlament -berichtete -China -aufgrund -Stellen -warum -Kindern -heraus -heutigen -Anteil -Herr -Öffentlichkeit -Abend -Liebe -rechnen -fällt -New York -Industrie -Stuttgarter -wären -Vorjahr -Sicht -Idee -Banken -verlassen -Leiter -Bühne -insbesondere -offen -stets -Theater -ändern -entschieden -Staaten -Experten -Gesetz -Geschäft -Tochter -angesichts -gelten -Mehr -erwarten -läuft -fordert -Japan -Sieg -Stimmen -wählen -russischen -gewinnen -CSU -bieten -Nähe -jährlich -Bremen -Schüler -Rede -Funktion -Zuschauer -hingegen -anderes -Führung -Besucher -Drittel -Moskau -immerhin -Vorsitzende -Urteil -Kultur -betonte -mittlerweile -Saison -Konzept -suchen -Zahlen -Roman -Gewalt -Köln -gesamte -indem -EU -Stunde -ehemalige -Auftrag -entscheiden -genannt -tragen -Börse -langen -häufig -Chancen -vor allem -Position -alt -Luft -Studenten -übernehmen -stärker -ohnehin -zeigte -geplanten -Reihe -darum -verhindern -begann -Medien -verkauft -Minister -wichtig -amerikanische -sah -gesamten -einst -verwendet -vorbei -Behörden -helfen -Folgen -bezeichnet -Ihnen -zur Zeit -voll -deutscher -Worten -plötzlich -müßte -Vertrag -Staatsanwaltschaft -Monat -Oder -Herbst -Israel -zahlen -Zeitung -Grenzen -Wissenschaftler -Partner -Patienten -nutzen -Bund -setzte -Betrieb -Michael -beteiligt -Professor -Fernsehen -Künstler -mehreren -erinnert -Liste -Möglichkeiten -Autor -täglich -eingesetzt -Versuch -Alter -Autos -Kohl -außer -Hoffnung -Verkauf -nennt -erscheint -führte -Prozeß -Täter -bisherigen -länger -erkennen -treffen -unser -begonnen -Antrag -beschäftigt -Opposition -Maßnahmen -brachte -nächste -gezeigt -Sinn -Erde -gefordert -Wohnung -all -Menge -gerne -Hintergrund -hören -Deutschlands -selten -bestätigt -bestimmt -Statt -entstehen -nannte -schreibt -Union -brauchen -gewählt -Kraft -elf -trägt -zieht -Grenze -Geschäftsführer -Team -Gebäude -Tonnen -Wettbewerb -Anspruch -Polen -morgen -Bremer -Wegen -Gebiet -glaubt -Sa -Natur -Arbeiten -jene -Fällen -Jahrhundert -leisten -Zeitpunkt -internationale -mein -Konkurrenz -nach wie vor -nicht einmal -stieg -notwendig -sogenannte -fahren -kostet -entsprechenden -geplante -geschlossen -Fehler -Zweifel -Erklärung -wiederum -erschienen -gehe -Glück -erfolgreich -fehlt -Gruppen -Aktion -kündigte -meinen -manchmal -verschiedene -übernommen -möglichst -SZ -lieber -Verantwortung -Gespräche -Suche -gern -fallen -Organisation -Preise -weitgehend -Basis -Computer -aufgenommen -Schutz -Eindruck -fiel -nahe -schlecht -lebt -Verhältnis -Forderung -britischen -sozialen -Technik -entstanden -Vorstand -verantwortlich -of -bestimmten -jüngsten -geboren -erhält -wobei -Gegner -Gründen -Material -Spitze -Gewinn -Punkten -vertreten -Schulen -Studie -Zeichen -gewonnen -Kurs -Washington -Türkei -Erfahrungen -wirkt -Armee -erhielt -beginnen -bißchen -entfernt -vorgesehen -Ergebnisse -zahlreiche -entgegen -hielt -privaten -sucht -Gemeinde -Antwort -Zentrum -bilden -legen -Schweiz -gemeinsamen -weg -teilweise -Großbritannien -Licht -Hannover -Produkte -Stimme -diesmal -Schluß -gingen -angeboten -Gesicht -Treffen -ließen -eröffnet -versuchen -Konzern -Leistungen -Gäste -Wohnungen -möglicherweise -wichtige -zog -geraten -Bewegung -nötig -Mitglied -hundert -Düsseldorf -PDS -wahrscheinlich -vermutlich -selber -ständig -Senat -Branche -Journalisten -enthält -laufen -Straßen -Einführung -arbeitet -verlangt -Werke -Runde -Besuch -unterstützt -Manager -Brüssel -de -schweren -sitzt -geschrieben -jemand -französische -Wachstum -taz -bestimmte -Drei -Freiheit -entsprechende -kennt -Band -Arbeitgeber -glauben -Wege -verurteilt -tritt -Volk -Pläne -R -angekündigt -Satz -sitzen -Vorschlag -rechnet -muss -erhöht -Investitionen -Aufgaben -warten -entdeckt -wichtigsten -Heimat -verstehen -schrieb -trotzdem -erfahren -Unternehmens -frühere -bekommt -Jugendlichen -Dinge -öffentliche -Institut -Wirkung -sorgen -trifft -reden -Werte -Forscher -Wo -Sitzung -zusätzlich -vielmehr -Anlaß -gehalten -Tel. -Schweizer -beschlossen -Serben -Bundestag -Verwaltung -vorher -automatisch -betont -Leistung -abgeschlossen -zufrieden -Beschäftigten -Papier -Thomas -Verbindung -Sinne -ziemlich -Debatte -enthalten -ausschließlich -bestehen -Flüchtlinge -Jugendliche -Freund -A -Themen -überall -droht -Gewerkschaften -Bedingungen -Beziehungen -Text -europäische -Punkt -Erfahrung -I -Außenminister -soziale -russische -Tisch -Republik -höher -reicht -Million -gehabt -politisch -folgen -ähnlich -geschaffen -Schaden -erster -Körper -Amerikaner -getötet -Frieden -zählt -Beteiligung -Wagen -Bahn -vergessen -Verlust -Tiere -Museum -gegründet -daraus -Start -Kontrolle -Einfluß -Österreich -Norden -Wunsch -manche -Wahlen -Reise -steigen -gemeinsame -Darstellung -Demokratie -König -England -nennen -Teile -Jahrhunderts -denken -Name -Lehrer -S. -laufenden -verbunden -Qualität -zusätzliche -Widerstand -schön -Spanien -gegangen -Beamten -Europas -Freunde -mitteilte -erklären -Familien -Gerade -gefallen -Aktie -Krise -Baden-Württemberg -Nummer -wies -außerhalb -gestiegen -Reform -Literatur -Arbeitslosigkeit -Lebens -Sozialdemokraten -gewann -Moment -angeblich -entsprechend -Mill -weiteres -beträgt -dritte -wesentlich -Amerika -Insel -britische -Nachfolger -entspricht -dahin -unseren -solcher -Juden -Sport -Rathaus -verkaufen -größere -Frühjahr -gebaut -Wahrheit -dar -Kommission -Polizisten -Bosnien -offensichtlich -Grundlage -relativ -Nein -Klasse -tut -hängt -wenigstens -aktuellen -Modell -Herstellung -hinzu -Gedanken -Übernahme -entwickeln -Quartal -meiner -dürften -jener -Zustimmung -legt -fordern -Einheit -blieben -Laut -in Zukunft -Szene -Klaus -Bundeskanzler -Schwierigkeiten -geblieben -lesen -getroffen -keineswegs -Abschluß -Verlag -Interessen -Netz -anschließend -Plan -Ausbildung -befindet -zunehmend -verzichten -gespielt -ermittelt -legte -Ruhe -Hans -Händler -genutzt -höhere -starke -treten -Veranstaltung -Stimmung -Ärzte -erheblich -standen -Teilnehmer -möchten -starken -Forderungen -Stand -Anlage -siehe -erfolgt -Vertrieb -beste -gelungen -Ordnung -Andreas -lautet -kritisiert -Schröder -machten -verstärkt -Süden -Haltung -freien -weist -nahezu -Motto -abgelehnt -gelang -gezogen -frühen -schreiben -aufs -gleiche -öffentlich -Häuser -kleiner -folgt -Einer -Ja -Wien -möglichen -weiterer -kaufen -Gefühl -wirtschaftlichen -Initiative -hofft -dürfe -Person -Telekom -kennen -interessiert -Beitrag -Geschäftsjahr -lernen -gefragt -bezeichnete -Meister -Bücher -kürzlich -Tradition -einzelne -Vorsitzender -Verband -Grüne -überzeugt -Rückkehr -schwere -falsch -unten -behandelt -indes -großes -Worte -getan -Vorstellung -somit -langsam -gedacht -geändert -hervor -Hessen -Städte -unbedingt -Hotel -Artikel -tätig -zahlreichen -Geburtstag -gesprochen -schließen -Engagement -Gründe -erlaubt -Ausländer -Anleger -größer -verlieren -Verhalten -ausgesprochen -fanden -Betriebe -Praxis -ums -galt -Gemeinden -Kanzler -Viertel -private -Zustand -miteinander -genauso -präsentiert -Planung -Forschung -staatlichen -Gott -einzigen -wichtigen -kosten -Verkehr -befinden -Kauf -Vorstellungen -Halle -Untersuchung -bessere -unserem -jeweiligen -angelegt -beendet -Fälle -Einschätzung -stehe -Dienst -herrscht -Begriff -Sekunden -Essen -ans -erfüllt -Krankenhaus -Auskunft -bauen -Rechnung -Arbeitnehmer -endgültig -Truppen -Jungen -Aufbau -leider -verpflichtet -Auffassung -komme -Karriere -bezahlen -Telefon -Rest -Umwelt -schlägt -bewußt -wichtiger -Sonne -trat -gelangen -schneller -Kandidaten -Brief -folgenden -erscheinen -wirtschaftliche -Nr. -Fahrer -erforderlich -Rennen -Nachfrage -Morgen -Verdacht -eigener -Hinweis -Umgebung -bestätigte -Regelung -Fenster -dienen -modernen -zuständig -Flughafen -Generation -gelegt -ergeben -nationalen -Waffen -Schicksal -Entscheidungen -Jugend -Niederlage -Spaß -Niveau -Gelände -Trend -heißen -Tatsache -Presse -Feuer -bezahlt -Förderung -Bildung -vorstellen -grundsätzlich -voller -Winter -italienischen -Botschaft -Investoren -Grad -Geschäfte -Leser -spielte -bayerischen -erzielt -reichen -Einrichtung -Energie -Gelegenheit -regelmäßig -übertragen -Freude -verändert -Umgang -Fans -entweder -Dauer -Finanzierung -Hersteller -Größe -eingestellt -Kontakt -früh -ursprünglich -Prinzip -Ausdruck -definieren -Mittelpunkt -Wunder -eindeutig -Quadratmeter -Mitarbeitern -Behandlung -größeren -Nachbarn -Schriftsteller -Himmel -hoffen -unterstützen -Auge -Risiko -allgemeinen -verdient -Tor -Finanzminister -behauptet -Rücktritt -schwarzen -bewegen -plus -mögliche -passiert -Angesichts -betroffen -durchgeführt -entsteht -Vorwürfe -näher -bekam -Aussage -Verfassung -Bewohner -vorhanden -richtige -übrigens -Kommunen -Städten -verfügt -Filme -Tabelle -historischen -Werbung -fragt -Gegenteil -Fischer -Gegensatz -Bauern -Arzt -Leitung -voraussichtlich -besondere -Abgeordneten -sehe -Arbeiter -Beschluß -festgenommen -Ermittlungen -Minute -Anzahl -Willen -Ruf -aktiv -einiger -westlichen -Schweden -technischen -verwenden -Vorwurf -zwanzig -höheren -offiziell -tief -in der Regel -Du -Ausbau -Auswahl -Voraussetzung -Wissenschaft -richtigen -schien -festgelegt -Bilanz -erhöhen -kurzem -Gang -vorerst -Hände -wirken -Vertrauen -denkt -lösen -Positionen -zählen -Veränderungen -besitzt -Alternative -Falle -Entwurf -Ziele -sichern -ernst -positiv -Tat -ermöglicht -Ideen -ausgeschlossen -Änderung -Ebene -äußerst -Lager -zweimal -Brandenburg -Tür -dringend -Regisseur -Wechsel -Begründung -alter -Wirklichkeit -fehlen -links -Summe -Aktivitäten -Erinnerung -Zugang -praktisch -geöffnet -Leipzig -Frank -Strecke -japanischen -fährt -Wende -Herz -Autoren -fürs -übrigen -Ausgabe -Fußball -kritisierte -Projekte -versteht -Stil -letztlich -Aussagen -Oberbürgermeister -herum -Bundeswehr -Verständnis -steigt -kämpfen -zugunsten -glaube -Fraktion -Posten -Dorf -Post -unmittelbar -Bruder -außen -welcher -Rang -Krankheit -eingerichtet -Auswirkungen -Strom -bloß -Auseinandersetzung -zumal -Struktur -Linie -Felder -Ministerpräsidenten -dient -wußte -falls -entstand -Herrn -Autofahrer -Information -Erwartungen -Sitz -definiert -warf -Fusion -Untersuchungen -gaben -verlor -los -daraufhin -Zinsen -scheinen -Sowjetunion -Büro -angenommen -Ursache -Steuern -fühlen -Anlagen -Franzosen -allzu -liege -verteilt -soviel -Landtag -Nürnberg -ab -Bereichen -stammt -Service -künftigen -Kino -Behörde -gehandelt -Nutzung -ausgerechnet -Interview -Wissen -zuerst -aufgefordert -Gewerkschaft -davor -Ausnahme -Erhöhung -wächst -gearbeitet -Rechte -eins -unterschiedlichen -geradezu -wirft -international -Zug -erreichte -Bedarf -erlebt -teuer -Kritiker -Flucht -Klage -Änderungen -informiert -geschickt -erinnern -Männern -Unternehmer -klingt -Hinweise -kräftig -fort -rein -eigenes -spät -Eröffnung -Beratung -Gefängnis -städtischen -angezeigt -rasch -vorgestellt -erfüllen -freie -fertig -Haushalt -berücksichtigt -schlagen -Direktor -Realität -Falls -Schreiben -Sender -Provinz -Job -festgestellt -Standort -Beruf -gestorben -voraus -gesucht -überwiegend -Stellung -Einigung -Vorschläge -Texte -fühlt -Schuld -leichter -vierten -Halbjahr -Rücken -Innenminister -Karten -halben -betrachtet -schlug -geschehen -Zeitraum -vermeiden -stattfinden -prüfen -unterdessen -ausländischen -vieler -Formen -Tore -starb \ No newline at end of file +ich 930 +sie 567 +das 516 +ist 509 +du 507 +nicht 444 +die 410 +es 377 +und 368 +der 286 +was 280 +wir 280 +zu 234 +ein 218 +er 215 +in 205 +mir 166 +mit 163 +ja 152 +den 152 +wie 152 +mich 145 +auf 143 +eine 136 +so 135 +aber 134 +hier 128 +dass 127 +hat 123 +sind 118 +dich 117 +von 117 +haben 116 +für 116 +wenn 115 +war 112 +ihr 107 +an 102 +habe 96 +bin 95 +nur 93 +da 93 +Nein 93 +noch 93 +dir 93 +hast 87 +einen 85 +uns 85 +sich 84 +dem 76 +kann 75 +auch 73 +gut 72 +schon 72 +sein 71 +jetzt 71 +aus 71 +ihn 70 +als 70 +dann 70 +mal 69 +bist 68 +um 67 +meine 67 +wird 65 +im 64 +doch 62 +mein 62 +alles 59 +keine 57 +oder 54 +weiß 54 +man 52 +nach 52 +werden 50 +nichts 49 +wo 49 +will 46 +also 46 +mehr 46 +etwas 46 +immer 44 +muss 43 +warum 43 +bei 43 +hab 42 +bitte 41 +wieder 41 +ihnen 42 +los 40 +machen 39 +diese 39 +vor 39 +können 38 +alle 37 +sagen 37 +zum 37 +gehen 37 +sehr 37 +denn 36 +geht 35 +sehen 35 +tun 35 +Mann 35 +euch 34 +wer 34 +ihm 34 +einem 34 +wirklich 34 +werde 34 +einer 33 +deine 33 +danke 33 +über 32 +ihre 32 +Komm 31 +vielleicht 31 +einfach 30 +soll 30 +müssen 30 +weg 29 +nie 29 +wissen 29 +gibt 29 +kein 29 +hey 28 +des 28 +tut 28 +Leben 28 +willst 27 +okay 27 +viel 27 +würde 27 +weil 27 +Zeit 27 +dein 27 +am 26 +hatte 26 +dieser 25 +nun 25 +kommt 25 +ok 25 +kannst 25 +kommen 25 +zurück 24 +heute 24 +damit 24 +ganz 24 +gesagt 23 +macht 23 +wurde 23 +weißt 23 +Leid 23 +bis 23 +wollen 23 +wäre 22 +wollte 22 +sicher 22 +Frau 22 +lch 21 +meinen 21 +Berlin 21 +lassen 21 +Leute 20 +genau 20 +seine 20 +zur 19 +zwei 19 +hätte 19 +Lass 19 +hallo 19 +könnte 18 +waren 18 +klar 18 +ab 17 +gerade 17 +glaube 17 +sollte 17 +Vater 17 +gesehen 17 +durch 16 +Morgen 16 +dieses 16 +raus 16 +Tag 16 +Geld 16 +gemacht 16 +schön 16 +liebe 16 +keinen 16 +reden 16 +sagte 16 +diesen 16 +wohl 16 +jemand 16 +unsere 16 +ob 15 +wirst 15 +mach 15 +paar 15 +hör 15 +besser 15 +passiert 15 +anderen 15 +dachte 15 +daß 15 +selbst 15 +Mutter 14 +wieso 14 +ohne 14 +möchte 14 +gehört 14 +her 14 +musst 14 +sag 14 +meiner 14 +richtig 14 +Ordnung 14 +lange 14 +helfen 13 +dort 13 +geben 13 +sieht 13 +Geh 13 +sei 13 +diesem 13 +finden 13 +meinem 13 +gar 13 +sagt 13 +weiter 13 +gleich 13 +gute 13 +vom 13 +Warte 12 +deinen 12 +ins 12 +denke 12 +Nacht 12 +natürlich 12 +davon 12 +hören 12 +Welt 12 +Mädchen 12 +seit 12 +hin 12 +viele 12 +sollten 12 +dafür 12 +machst 12 +sollen 12 +zusammen 12 +schnell 12 +wegen 11 +Menschen 11 +Hause 11 +bleiben 11 +essen 11 +unser 11 +ganze 11 +habt 11 +Freund 11 +Angst 11 +tot 11 +andere 11 +unter 11 +drei 11 +ihren 11 +bringen 10 +rein 10 +Haus 10 +nehmen 10 +geht’s 10 +eines 10 +seid 10 +genug 10 +brauchen 10 +getan 10 +dabei 10 +verdammt 10 +stimmt 10 +Moment 10 +Kinder 10 +Arbeit 10 +erst 10 +guten 10 +Problem 10 +gegen 10 +Herr 10 +glauben 9 +sofort 9 +darf 9 +Abend 9 +hatten 9 +fertig 9 +niemand 9 +warten 9 +sonst 9 +Halt 9 +jeder 9 +steht 9 +’ne 9 +einmal 9 +Scheiße 9 +bevor 9 +eigentlich 9 +Junge 9 +fragen 9 +brauche 9 +Jahre 9 +Bruder 9 +beim 9 +Sache 9 +heißt 9 +Sohn 9 +siehst 9 +warst 8 +halten 8 +mag 8 +sprechen 8 +gib 8 +konnte 8 +daran 8 +sterben 8 +darüber 8 +Wann 8 +Kopf 8 +Hilfe 8 +deiner 8 +Familie 8 +deinem 8 +Baby 8 +kennen 8 +gefunden 8 +sieh 8 +kam 8 +vergessen 8 +könnten 8 +dazu 8 +egal 8 +später 8 +allein 8 +Jahren 8 +Kind 8 +seinen 8 +töten 8 +Minuten 8 +beide 8 +Stadt 8 +denken 8 +toll 8 +gern 8 +verstehe 8 +würden 8 +ihrer 8 +Dank 7 +jeden 7 +Ende 7 +Dinge 7 +Namen 7 +wahr 7 +lieber 7 +letzte 7 +runter 7 +all 7 +bekommen 7 +Männer 7 +kleine 7 +eins 7 +gab 7 +wusste 7 +sehe 7 +Augen 7 +Freunde 7 +darauf 7 +Jungs 7 +Glück 7 +meinst 7 +Auto 7 +sage 7 +bald 7 +gehe 7 +komme 7 +mache 7 +fahren 7 +Uhr 7 +Teufel 7 +Art 7 +bereit 7 +hört 7 +einzige 7 +eure 7 +Fall 7 +Film 7 +echt 7 +bisschen 7 +weit 7 +vielen 7 +ihrem 7 +vorbei 6 +drin 6 +hinter 6 +stehen 6 +Name 6 +ganzen 6 +treffen 6 +sogar 6 +denkst 6 +Musik 6 +verstanden 6 +Tür 6 +etwa 6 +solltest 6 +verrückt 6 +spielen 6 +nimm 6 +fast 6 +ruhig 6 +arbeiten 6 +dran 6 +Sorgen 6 +hätten 6 +lang 6 +deshalb 6 +ziemlich 6 +Kerl 6 +darum 6 +Job 6 +verloren 6 +wurden 6 +kenne 6 +Ahnung 6 +seiner 6 +Grund 6 +überhaupt 6 +finde 6 +neue 6 +jemanden 6 +je 6 +Hand 6 +woher 6 +Frauen 6 +Idee 6 +draußen 6 +Land 6 +Typ 6 +spät 6 +gekommen 6 +Schwester 6 +Wasser 6 +niemals 6 +Spiel 6 +Geschichte 6 +seinem 6 +kommst 6 +liegt 6 +keiner 6 +recht 6 +Recht 6 +sah 6 +Seite 6 +wenig 6 +verstehen 6 +endlich 6 +kurz 6 +welche 6 +bestimmt 6 +versuchen 6 +erste 6 +Ruhe 5 +nett 5 +versucht 5 +Tochter 5 +hoch 5 +bringt 5 +ging 5 +Jahr 5 +braucht 5 +würdest 5 +dies 5 +lasst 5 +Alter 5 +große 5 +anders 5 +Polizei 5 +Stunden 5 +musste 5 +während 5 +gewesen 5 +zeigen 5 +gerne 5 +suchen 5 +erzählt 5 +beste 5 +Tod 5 +Ort 5 +bleibt 5 +Chance 5 +bedeutet 5 +sagst 5 +letzten 5 +holen 5 +fünf 5 +Krieg 5 +ersten 5 +Sachen 5 +gefallen 5 +Wahrheit 5 +glaubst 5 +zwischen 5 +denen 5 +Spaß 5 +gehst 5 +oben 5 +kleinen 5 +Mama 5 +Yeah 5 +schwer 5 +Schule 5 +eben 5 +Wagen 5 +Gesicht 5 +manchmal 5 +guter 5 +irgendwas 5 +gefällt 5 +Schuld 5 +weiss 5 +Woche 5 +Entschuldigung 5 +schlecht 5 +vier 5 +getötet 5 +hoffe 5 +erzählen 5 +hättest 5 +Zimmer 5 +unserer 5 +Tage 5 +Teil 5 +einige 5 +verlassen 5 +gestern 5 +wollten 5 +lässt 5 +Wort 5 +Schatz 5 +allen 5 +Entschuldigen 5 +ernst 5 +scheint 5 +kleiner 5 +bleib 5 +kriegen 5 +irgendwie 5 +läuft 5 +Freundin 5 +Mensch 4 +beiden 4 +Ding 4 +muß 4 +fest 4 +Herz 4 +Waffe 4 +Dollar 4 +Sorge 4 +tust 4 +nehme 4 +oft 4 +drauf 4 +anderes 4 +unten 4 +tu 4 +stellen 4 +wichtig 4 +brauchst 4 +falls 4 +falsch 4 +wen 4 +gedacht 4 +Nummer 4 +wohin 4 +Hände 4 +Blut 4 +bloß 4 +kennst 4 +Bett 4 +zuerst 4 +Bring 4 +Eltern 4 +Plan 4 +alte 4 +euer 4 +neuen 4 +rede 4 +retten 4 +trinken 4 +wären 4 +Fehler 4 +ehrlich 4 +gibt’s 4 +gegeben 4 +besten 4 +glücklich 4 +alt 4 +seht 4 +Platz 4 +wartet 4 +wahrscheinlich 4 +unseren 4 +alten 4 +schlafen 4 +nächsten 4 +nächste 4 +frei 4 +Pass 4 +früher 4 +bereits 4 +alleine 4 +sondern 4 +Jungen 4 +langsam 4 +möglich 4 +lernen 4 +Papa 4 +hierher 4 +wollt 4 +Schiff 4 +kaum 4 +jede 4 +Menge 4 +trotzdem 4 +Typen 4 +Klasse 4 +willkommen 4 +Stunde 4 +Szene 4 +Hölle 4 +außer 4 +geworden 4 +Telefon 4 +denkt 4 +überall 4 +Feuer 4 +setzen 4 +frage 4 +Frage 3 +Ruf 3 +könntest 3 +Probleme 3 +Stelle 3 +laufen 3 +klingt 3 +nennen 3 +zwar 3 +erinnern 3 +Folge 3 +eigenen 3 +Körper 3 +gebe 3 +hattest 3 +Kumpel 3 +Hund 3 +nochmal 3 +hab’s 3 +aufhören 3 +früh 3 +tat 3 +meines 3 +zehn 3 +direkt 3 +Wiedersehen 3 +Luft 3 +neues 3 +könnt 3 +sechs 3 +verstehst 3 +dürfen 3 +Meister 3 +Erde 3 +wolltest 3 +Zukunft 3 +funktioniert 3 +voll 3 +reicht 3 +kleines 3 +wem 3 +großen 3 +gutes 3 +Büro 3 +richtige 3 +Waffen 3 +danach 3 +Wahl 3 +verlieren 3 +versuche 3 +Wochen 3 +kämpfen 3 +cool 3 +Stück 3 +allem 3 +lieben 3 +hol 3 +getroffen 3 +damals 3 +ziehen 3 +geschafft 3 +tue 3 +tja 3 +Entschuldige 3 +verschwinden 3 +Boss 3 +nachdem 3 +vergiss 3 +passieren 3 +Licht 3 +lustig 3 +gebracht 3 +stark 3 +Gefühl 3 +völlig 3 +Schlüssel 3 +großartig 3 +drüben 3 +halte 3 +irgendwo 3 +Buch 3 +Leuten 3 +suche 3 +jedes 3 +spielt 3 +genauso 3 +krank 3 +gegangen 3 +fand 3 +wovon 3 +unglaublich 3 +fühle 3 +leider 3 +Himmel 3 +böse 3 +froh 3 +leicht 3 +sobald 3 +vertrauen 3 +Krankenhaus 3 +genommen 3 +Arzt 3 +unserem 3 +Idiot 3 +solche 3 +großer 3 +groß 3 +Rest 3 +Scheiß 3 +Tagen 3 +Doktor 3 +wert 3 +spricht 3 +hält 3 +Team 3 +rufen 3 +heiraten 3 +Stimme 3 +deswegen 3 +Mist 3 +Straße 3 +König 3 +redest 3 +solange 3 +lebt 3 +erklären 3 +Millionen 3 +Onkel 3 +besonders 3 +glaub 3 +wisst 3 +sitzen 3 +folgen 3 +weniger 3 +Kaffee 3 +tragen 3 +Zeug 3 +Michael 3 +kaufen 3 +Liste 3 +ändern 3 +super 3 +Kampf 3 +fliegen 3 +findet 3 +hasse 3 +Person 3 +Mund 3 +vorsichtig 3 +hab’ 3 +Präsident 3 +umbringen 3 +Boden 3 +machte 3 +mögen 3 +stolz 3 +Party 3 +gefragt 3 +hörst 3 +schicken 3 +kümmern 3 +Frank 3 +kennt 3 +verletzt 3 +Nachricht 3 +schneller 3 +hart 3 +lasse 3 +total 3 +Gefängnis 3 +Sicherheit 3 +möchten 3 +schreiben 3 +wenigstens 3 +interessiert 3 +sagten 3 +weisst 3 +Augenblick 3 +angerufen 3 +unmöglich 3 +verdammte 3 +Bild 3 +komisch 3 +Sinn 3 +vorstellen 3 +Traum 3 +länger 3 +möchtest 3 +anrufen 3 +eher 3 +schöne 3 +sollst 3 +gestorben 3 +Tages 3 +war’s 3 +Kraft 3 +gesprochen 3 +denk 3 +heraus 3 +schau 3 +Schau 3 +wärst 2 +außerdem 2 +heißen 2 +Liebling 2 +passt 2 +Setz 2 +tatsächlich 2 +lesen 2 +obwohl 2 +glaubt 2 +erwartet 2 +meisten 2 +bringe 2 +still 2 +umgebracht 2 +plötzlich 2 +General 2 +Bewegung 2 +führen 2 +höre 2 +Geschäft 2 +Versuch 2 +werdet 2 +tolle 2 +konnten 2 +anfangen 2 +hinten 2 +perfekt 2 +fällt 2 +erinnerst 2 +Sekunden 2 +gerettet 2 +jemals 2 +Meinung 2 +stand 2 +geschehen 2 +Antwort 2 +schlimm 2 +gewinnen 2 +Gedanken 2 +erinnere 2 +Herren 2 +vorher 2 +Auge 2 +Anfang 2 +Lebens 2 +Monate 2 +benutzen 2 +fühlen 2 +Ärger 2 +arbeite 2 +acht 2 +Rolle 2 +gefährlich 2 +gelernt 2 +fallen 2 +werd 2 +Nähe 2 +geschickt 2 +Hilf 2 +liebt 2 +absolut 2 +Tisch 2 +Raum 2 +Zeig 2 +Ziel 2 +jedem 2 +versprochen 2 +Wohnung 2 +erfahren 2 +zieh 2 +dumm 2 +schauen 2 +laß 2 +herum 2 +Klappe 2 +Laden 2 +magst 2 +aufs 2 +arbeitet 2 +zahlen 2 +sie’s 2 +Worte 2 +Flugzeug 2 +bezahlen 2 +Firma 2 +Regeln 2 +laut 2 +sowas 2 +findest 2 +starb 2 +Schluss 2 +Fenster 2 +liegen 2 +welcher 2 +wär’s 2 +niemanden 2 +Verzeihung 2 +benutzt 2 +Finger 2 +schätze 2 +lachen 2 +rüber 2 +Witz 2 +Kontrolle 2 +nimmt 2 +fangen 2 +Herzen 2 +gewonnen 2 +Bier 2 +Arm 2 +kamen 2 +gleiche 2 +bitten 2 +fort 2 +fahr 2 +Informationen 2 +steh 2 +neu 2 +hörte 2 +rum 2 +Lage 2 +bekommt 2 +sowieso 2 +dagegen 2 +nötig 2 +Richtung 2 +aussehen 2 +Minute 2 +wach 2 +sieben 2 +behalten 2 +links 2 +selber 2 +drehen 2 +verkaufen 2 +jedenfalls 2 +geschrieben 2 +Entscheidung 2 +unterwegs 2 +Insel 2 +Hotel 2 +voller 2 +Monat 2 +verschwinde 2 +erzähl 2 +rufe 2 +Lauf 2 +wunderbar 2 +gearbeitet 2 +verdient 2 +legen 2 +wünschte 2 +Seele 2 +wofür 2 +Gefahr 2 +Geist 2 +gelesen 2 +bezahlt 2 +Schicksal 2 +Monaten 2 +Weile 2 +Agent 2 +mussten 2 +verheiratet 2 +Arme 2 +Ehe 2 +jemandem 2 +Beispiel 2 +welches 2 +wozu 2 +kalt 2 +sitzt 2 +Anruf 2 +Sekunde 2 +nächstes 2 +hole 2 +Bauer 2 +bisher 2 +Karte 2 +aller 2 +du’s 2 +Befehl 2 +sucht 2 +darfst 2 +tanzen 2 +bekannt 2 +Opfer 2 +Leg 2 +steckt 2 +seien 2 +unbedingt 2 +Zug 2 +witzig 2 +Amerika 2 +führt 2 +Kamera 2 +stell 2 +schlagen 2 +kriegt 2 +irgendetwas 2 +nennt 2 +bin’s 2 +Gefühle 2 +ständig 2 +verändert 2 +diesmal 2 +heiß 2 +Partner 2 +Mörder 2 +Computer 2 +dahin 2 +ansehen 2 +klein 2 +zerstört 2 +nämlich 2 +öffnen 2 +schlimmer 2 +Unfall 2 +müsste 2 +Nase 2 +Vorsicht 2 +schießen 2 +Geburtstag 2 +beschützen 2 +müde 2 +Beziehung 2 +gestohlen 2 +Geheimnis 2 +stirbt 2 +zieht 2 +süß 2 +Geschenk 2 +offen 2 +Punkt 2 +Dame 2 +hilft 2 +bewegen 2 +sauer 2 +Tasche 2 +Schritt 2 +übrigens 2 +meinte 2 +müsst 2 +schönen 2 +erwischt 2 +gehabt 2 +Bart 2 +erreichen 2 +wär 2 +irgendwann 2 +Gebäude 2 +Schlag 2 +Hoffentlich 2 +lügen 2 +interessant 2 +liebst 2 +erledigt 2 +Armee 2 +näher 2 +nahm 2 +weitere 2 +Chef 2 +Rücken 2 +Preis 2 +Wunder 2 +Bescheid 2 +Verbindung 2 +Boot 2 +holt 2 +normal 2 +Situation 2 +erledigen 2 +mußt 2 +Ehre 2 +darin 2 +fürs 2 +Planeten 2 +ungefähr 2 +ich’s 2 +kriegst 2 +Haut 2 +Ben 2 +Prinzessin 2 +Drogen 2 +euren 2 +Mord 2 +Reise 2 +seltsam 2 +schwöre 2 +Schlampe 2 +Zeiten 2 +verliebt 2 +Position 2 +redet 2 +Toten 2 +Möglichkeit 2 +töte 2 +stimmt’s 2 +Sonne 2 +Willen 2 +Haltet 2 +letzter 2 +Kontakt 2 +Damen 2 +Zuhause 2 +Antworten 2 +zumindest 2 +gekauft 2 +Schwert 2 +Hunger 2 +Frieden 2 +hau 2 +Blick 2 +persönlich 2 +Nachrichten 2 +Maul 2 +verraten 2 +Hoffnung 2 +Frag 2 +geboren 2 +Untertitel 2 +kaputt 2 +eigene 2 +Soldaten 2 +ans 2 +übrig 2 +hältst 2 +aufhalten 2 +Zeichen 2 +schade 2 +dauert 2 +Band 2 +Schwein 2 +versteckt 2 +Haare 2 +wütend 2 +Hochzeit 2 +fühlt 2 +schrecklich 2 +Energie 2 +Leiche 2 +sauber 2 +traurig 2 +reich 2 +offensichtlich 2 +Bord 2 +gesucht 2 +beschäftigt 2 +erwarten 2 +manche 2 +verschwunden 2 +Professor 2 +Brief 2 +geredet 2 +zeigt 2 +ihres 2 +hübsch 2 +Show 2 +Ross 2 +sagtest 2 +verkauft 2 +namens 2 +Fernsehen 2 +versteht 2 +Schaut 2 +fährt 2 +heiße 2 +ließ 2 +nervös 2 +soweit 2 +fehlt 2 +gegessen 2 +setzt 2 +Vergangenheit 2 +deines 2 +Freut 2 +miteinander 2 +Handy 2 +neben 2 +anderer 2 +tief 2 +ran 2 +schaffen 2 +wette 1 +Wette 1 +Schaffen 1 +Aufgabe 1 +Entweder 1 +Foto 1 +richtigen 1 +Nehmt 1 +Tee 1 +verdammten 1 +Fuß 1 +schließlich 1 +brauch 1 +geschlafen 1 +letztes 1 +Beine 1 +irgendwelche 1 +Kindern 1 +ewig 1 +schlechte 1 +Unsinn 1 +Bombe 1 +Guck 1 +bekam 1 +Herrn 1 +gelassen 1 +angefangen 1 +Tante 1 +Hals 1 +Bank 1 +vorne 1 +stellt 1 +Weise 1 +Paris 1 +passen 1 +verantwortlich 1 +schwierig 1 +bleibe 1 +seh 1 +Operation 1 +rechts 1 +Anwalt 1 +verspreche 1 +bleibst 1 +Glückwunsch 1 +entscheiden 1 +allerdings 1 +entfernt 1 +brachte 1 +zerstören 1 +aussieht 1 +gespielt 1 +Lied 1 +schönes 1 +Lust 1 +Vaters 1 +Bilder 1 +kriege 1 +Feind 1 +Händen 1 +jung 1 +süße 1 +worauf 1 +Ball 1 +stecken 1 +geholfen 1 +gehören 1 +gleichen 1 +genannt 1 +heim 1 +nahe 1 +Aufnahme 1 +Achtung 1 +erster 1 +Mission 1 +bekomme 1 +Schmerz 1 +Fleisch 1 +Jawohl 1 +fantastisch 1 +Weihnachten 1 +Rennen 1 +Schwierigkeiten 1 +Park 1 +meint 1 +wussten 1 +Gesellschaft 1 +gebeten 1 +erhalten 1 +bedeuten 1 +verstecken 1 +Respekt 1 +dasselbe 1 +entschieden 1 +Gehirn 1 +dachten 1 +Witze 1 +Rat 1 +Volk 1 +beeil 1 +lebe 1 +Präsidenten 1 +dauern 1 +Monster 1 +Schuhe 1 +Übersetzt 1 +Beweise 1 +Hälfte 1 +Kirche 1 +Hintern 1 +Spur 1 +Lehrer 1 +Gold 1 +lieb 1 +neun 1 +Güte 1 +fahre 1 +geliebt 1 +geschlagen 1 +Loch 1 +normalerweise 1 +ganzes 1 +beweisen 1 +Colonel 1 +wunderschön 1 +gegenüber 1 +Bus 1 +großes 1 +Verstand 1 +spiele 1 +trägt 1 +gewartet 1 +herausfinden 1 +vermutlich 1 +niemandem 1 +Gegend 1 +bewegt 1 +Kate 1 +Fotos 1 +Angriff 1 +Grad 1 +nah 1 +Versprechen 1 +Schlaf 1 +Wind 1 +beginnt 1 +fürchte 1 +lag 1 +Messer 1 +überrascht 1 +Worüber 1 +Richard 1 +gefangen 1 +Bein 1 +Eier 1 +singen 1 +Dorf 1 +Überraschung 1 +besorgen 1 +Glas 1 +Besonderes 1 +besuchen 1 +geändert 1 +beginnen 1 +echte 1 +Pferd 1 +Held 1 +tötet 1 +Taxi 1 +Freunden 1 +mitnehmen 1 +Gruppe 1 +besorgt 1 +System 1 +fang 1 +ruft 1 +nimmst 1 +falschen 1 +leisten 1 +Adresse 1 +schwanger 1 +gemeinsam 1 +Stimmen 1 +entkommen 1 +übel 1 +Küche 1 +vermisst 1 +Besuch 1 +Flug 1 +hängt 1 +werfen 1 +falsche 1 +Ring 1 +Gebt 1 +überleben 1 +Lächeln 1 +Gesetz 1 +Tor 1 +Maschine 1 +Patienten 1 +Wald 1 +gehts 1 +Dach 1 +Schauspieler 1 +kannte 1 +Übersetzung 1 +drinnen 1 +wir’s 1 +Armen 1 +schwarzen 1 +blöd 1 +Blumen 1 +Freiheit 1 +Ecke 1 +seines 1 +Steig 1 +Knast 1 +Karten 1 +helfe 1 +Wein 1 +Vergnügen 1 +Unterschied 1 +gibst 1 +Verbrechen 1 +gesund 1 +sag’s 1 +zweite 1 +erkennen 1 +eurer 1 +welchen 1 +Meter 1 +Schüler 1 +weder 1 +Bar 1 +davor 1 +Mut 1 +wohnen 1 +statt 1 +Auftrag 1 +Pause 1 +wünsche 1 +Wand 1 +Männern 1 +schreit 1 +kostet 1 +spreche 1 +Erfolg 1 +einverstanden 1 +Wieviel 1 +Eis 1 +entlang 1 +erschossen 1 +teilen 1 +’nem 1 +Abendessen 1 +herein 1 +Mistkerl 1 +übernehmen 1 +danken 1 +gebaut 1 +Tritt 1 +beenden 1 +schützen 1 +weißen 1 +Verzeihen 1 +gefahren 1 +Polizist 1 +Bericht 1 +wüsste 1 +trifft 1 +Hut 1 +Meer 1 +lief 1 +zuvor 1 +schwarze 1 +wusstest 1 +Doug 1 +verdammter 1 +Zeitung 1 +bauen 1 +schuldig 1 +Freude 1 +zufrieden 1 +Drink 1 +versuchte 1 +gemeint 1 +indem 1 +sprach 1 +fühlst 1 +deren 1 +furchtbar 1 +Gericht 1 +hielt 1 +kapiert 1 +Kugel 1 +Quatsch 1 +Ohren 1 +verpasst 1 +fanden 1 +neuer 1 +Rose 1 +Drehbuch 1 +Haufen 1 +Schätzchen 1 +Haar 1 +Ehemann 1 +fassen 1 +stehe 1 +fragte 1 +funktionieren 1 +verhaftet 1 +drüber 1 +Sergeant 1 +feiern 1 +Major 1 +Verantwortung 1 +Bücher 1 +ähnlich 1 +kümmere 1 +unterhalten 1 +kümmert 1 +Stein 1 +Vorwärts 1 +gewusst 1 +schöner 1 +aufpassen 1 +Katze 1 +Brüder 1 +geschieht 1 +mitgebracht 1 +unseres 1 +dessen 1 +mitten 1 +erinnert 1 +Neuigkeiten 1 +vorhin 1 +Wahnsinn 1 +irgendjemand 1 +Angebot 1 +dadurch 1 +Kleid 1 +zufällig 1 +erklärt 1 +Schaden 1 +Restaurant 1 +Brücke 1 +melden 1 +Serie 1 +Erinnerungen 1 +gestellt 1 +besteht 1 +daher 1 +Schmerzen 1 +verdienen 1 +Fisch 1 +Gelegenheit 1 +hängen 1 +bekommst 1 +konntest 1 +Idioten 1 +gingen 1 +größte 1 +Virus 1 +betrunken 1 +geplant 1 +Strand 1 +fängt 1 +bringst 1 +keinem 1 +netter 1 +Thema 1 +rauchen 1 +machten 1 +Figur 1 +weiße 1 +brechen 1 +angetan 1 +lacht 1 +Druck 1 +worum 1 +mitkommen 1 +taten 1 +Fluss 1 +Mark 1 +verschwindet 1 +Mach’s 1 +traf 1 +wirkt 1 +kosten 1 +gelaufen 1 +Gewalt 1 +Daten 1 +Großvater 1 +Hunde 1 +Falle 1 +nachgedacht 1 +wahre 1 +eigenes 1 +Entschuldigt 1 +Gang 1 +erreicht 1 +sahen 1 +einzigen 1 +schlechter 1 +solchen 1 +ermordet 1 +längst 1 +Tiere 1 +Autos 1 +fiel 1 +wohnt 1 +Hose 1 +leiden 1 +freue 1 +seitdem 1 +sprichst 1 +Baum 1 +gebrochen 1 +gehalten 1 +Reihe 1 +Träume 1 +gezeigt 1 +halb 1 +Medizin 1 +hieß 1 +Meilen 1 +Bastard 1 +Befehle 1 +Wunsch 1 +rief 1 +Frühstück 1 +schaffst 1 +rot 1 +beruhigen 1 +hat’s 1 +aufgeben 1 +Gespräch 1 +Erfahrung 1 +Terroristen 1 +verbringen 1 +Besseres 1 +brauchte 1 +schätzen 1 +Bad 1 +Tote 1 +Anna 1 +Vogel 1 +Roger 1 +Wochenende 1 +vollkommen 1 +zweiten 1 +schläft 1 +verfolgen 1 +bemerkt 1 +Marge 1 +Signal 1 +Wichser 1 +Filme 1 +lächerlich 1 +Pistole 1 +schließen 1 +geschlossen 1 +Detective 1 +fragt 1 +Beweis 1 +President 1 +weinen 1 +wichtiger 1 +Verhalten 1 +Zeitpunkt 1 +Pläne 1 +Club 1 +schlägt 1 +küssen 1 +Scherz 1 +behandelt 1 +halbe 1 +fair 1 +Farbe 1 +weiteren 1 +entdeckt 1 +Natur 1 +Sara 1 +abgesehen 1 +Seiten 1 +bessere 1 +Kuchen 1 +langweilig 1 +selben 1 +Bürgermeister 1 +treten 1 +lautet 1 +größer 1 +Rechte 1 +Knie 1 +stehlen 1 +Regen 1 +hingehen 1 +gefeuert 1 +Koffer 1 +Geschäfte 1 +verfolgt 1 +umsonst 1 +dorthin 1 +Million 1 +bißchen 1 +daraus 1 +hinaus 1 +Lösung 1 +leer 1 +geblieben 1 +Krankheit 1 +hasst 1 +Kräfte 1 +rauf 1 +Sieg 1 +passierte 1 +Füße 1 +Zufall 1 +geschossen 1 +offenbar 1 +dankbar 1 +einiges 1 +heisst 1 +See 1 +Erklärung 1 +Flughafen 1 +mochte 1 +Englisch 1 +Flasche 1 +gebrauchen 1 +Soldat 1 +sterbe 1 +wurdest 1 +getrunken 1 +Kurs 1 +Womit 1 +isst 1 +ernsthaft 1 +fühlte 1 +fliegt 1 +Gewissen 1 +reichen 1 +derjenige 1 +Erinnerung 1 +einigen 1 +Geschichten 1 +nachts 1 +prima 1 +Schande 1 +Story 1 +Toilette 1 +fern 1 +peinlich 1 +wenn’s 1 +Dreck 1 +welchem 1 +Doc 1 +schafft 1 +Mädels 1 +anscheinend 1 +Lüge 1 +solltet 1 +bösen 1 +verhindern 1 +erwähnt 1 +verflucht 1 +langer 1 +Pete 1 +Tatsache 1 +Code 1 +eingeladen 1 +erlaubt 1 +angegriffen 1 +Anzug 1 +Pflicht 1 +Einsatz 1 +Direktor 1 +Königin 1 +Zweifel 1 +gewinnt 1 +deutsche 1 +erschießen 1 +Schloss 1 +dreht 1 +Fähigkeiten 1 +Trottel 1 +versteh 1 +klug 1 +Form 1 +zulassen 1 +Runde 1 +hassen 1 +lösen 1 +scharf 1 +verlangt 1 +Karriere 1 +Richter 1 +Tier 1 +wünschen 1 +Stock 1 +vorbereitet 1 +Interesse 1 +Panik 1 +Sprache 1 +vertraut 1 +freuen 1 +kontrollieren 1 +stören 1 +überprüfen 1 +Zustand 1 +gilt 1 +Risiko 1 +Schuss 1 +Mannes 1 +überlebt 1 +Opa 1 +Such 1 +zurückkommen 1 +erzählte 1 +gelogen 1 +Knochen 1 +Kunden 1 +gekriegt 1 +nachdenken 1 +Wesen 1 +Braut 1 +Oma 1 +wahnsinnig 1 +Polizisten 1 +Streit 1 +stehst 1 +hinterlassen 1 +dick 1 +jagen 1 +toller 1 +nutzen 1 +enden 1 +Blödsinn 1 +woran 1 +gebraucht 1 +Rache 1 +Sommer 1 +irre 1 +Urlaub 1 +Kino 1 +Radio 1 +merken 1 +abholen 1 +zeige 1 +kann’s 1 +Keller 1 +stoppen 1 +Labor 1 +Stopp 1 +schwach 1 +verliert 1 +geschah 1 +schreien 1 +Milch 1 +geklaut 1 +hinein 1 +Punkte 1 +Kerle 1 +Publikum 1 +schlage 1 +Gebiet 1 +scheinen 1 +echten 1 +schaffe 1 +Staaten 1 +einfacher 1 +entführt 1 +fragst 1 +Dienst 1 +überlegen 1 +vermisse 1 +wart 1 +weshalb 1 +beendet 1 +kompliziert 1 +warm 1 +Anführer 1 +Feinde 1 +Vertrag 1 +außerhalb 1 +geglaubt 1 +einsam 1 +reparieren 1 +soviel 1 +Washington 1 +ausgehen 1 +bat 1 +Gäste 1 +geheiratet 1 +Programm 1 +stärker 1 +überzeugt 1 +Akte 1 +Trick 1 +verschiedene 1 +Großmutter 1 +Lager 1 +fing 1 +knapp 1 +hoffen 1 +Berg 1 +Ehren 1 +erwachsen 1 +handelt 1 +Fahrer 1 +Fernseher 1 +gäbe 1 +Götter 1 +lebst 1 +gezogen 1 +Schutz 1 +trotz 1 +Zugang 1 +geb 1 +Song 1 +annehmen 1 +Geheimnisse 1 +Verräter 1 +blind 1 +Riesen 1 +schickt 1 +gesamte 1 +behandeln 1 +Kiste 1 +verändern 1 +zählt 1 +Grenze 1 +Bauch 1 +schick 1 +Nachmittag 1 +riecht 1 +schrieb 1 +vergeben 1 +Video 1 +Vorstellung 1 +erstes 1 +landen 1 +Sendung 1 +echter 1 +Fahrt 1 +woanders 1 +Affen 1 +getrennt 1 +komplett 1 +liebte 1 +dringend 1 +gedreht 1 +Prinz 1 +Sprich 1 +Gas 1 +nackt 1 +schlechten 1 +verbunden 1 +Deckung 1 +Gras 1 +schicke 1 +Beeilung 1 +suchst 1 +verletzen 1 +desto 1 +trage 1 +aufgenommen 1 +fliehen 1 +Gewehr 1 +springen 1 +Zunge 1 +merkwürdig 1 +weiterhin 1 +Star 1 +Stuhl 1 +irgend 1 +Tode 1 +Mühe 1 +teuer 1 +ändert 1 +Episode 1 +erzähle 1 +nirgendwo 1 +Zähne 1 +Schnitt 1 +mindestens 1 +Aufmerksamkeit 1 +Helden 1 +satt 1 +blöde 1 +Freundschaft 1 +freundlich 1 +treffe 1 +folgt 1 +Leitung 1 +gesetzt 1 +Norden 1 +Szenen 1 +Roboter 1 +verursacht 1 +Rechnung 1 +Hexe 1 +trauen 1 +Bedeutung 1 +Dieb 1 +junger 1 +nehm 1 +Ohr 1 +Dingen 1 +klappt 1 +Schlange 1 +arbeitest 1 +Grab 1 +morgens 1 +Pilot 1 +bewusst 1 +extra 1 +Heilige 1 +endet 1 +halben 1 +verlässt 1 +Absicht 1 +Ideen 1 +Mond 1 +Selbstmord 1 +Einheit 1 +Projekt 1 +Walter 1 +gehofft 1 +Pferde 1 +umgehen 1 +Aufnahmen 1 +Explosion 1 +Kämpfe 1 +Leichen 1 +Schatten 1 +Geister 1 +Kleider 1 +schweigen 1 +Türen 1 +wählen 1 +erzählst 1 +Garten 1 +schief 1 +zweimal 1 +begann 1 +Schönheit 1 +Bestes 1 +existiert 1 +Bühne 1 +langen 1 +mitgenommen 1 +Nachbarn 1 +Ton 1 +Brust 1 +gemein 1 +handeln 1 +schien 1 +unternehmen 1 +aufgehört 1 +bester 1 +reinkommen 1 +befindet 1 +Hubschrauber 1 +weitermachen 1 +einander 1 +Genie 1 +Wirklichkeit 1 +spielst 1 +Verbrecher 1 +aufgeregt 1 +gegenseitig 1 +Größe 1 +Ärzte 1 +Erlaubnis 1 +Hemd 1 +Killer 1 +Unterstützung 1 +fuhr 1 +größten 1 +Kapitän 1 +schreibt 1 +entlassen 1 +fährst 1 +nachher 1 +beobachtet 1 +entwickelt 1 +Freitag 1 +Nerven 1 +Stop 1 +Text 1 +Tränen 1 +Mitte 1 +Nee 1 +Gegner 1 +geraten 1 +Gast 1 +herauszufinden 1 +blieb 1 +Karma 1 +Strom 1 +besprechen 1 +Kunst 1 +müssten 1 +Schnauze 1 +Alkohol 1 +Jacke 1 +Träumen 1 +Prozent 1 +kenn 1 +Papier 1 +Stich 1 +Zelle 1 +angeht 1 +Geduld 1 +verboten 1 +menschliche 1 +Theater 1 +Agenten 1 +gerufen 1 +schlau 1 +Emily 1 +Herzlichen 1 +Staffel 1 +verpassen 1 +bricht 1 +ertragen 1 +Menschheit 1 +wußte 1 +Hoheit 1 +verwirrt 1 +Abteilung 1 +beeilen 1 +abgeschlossen 1 +abhauen 1 +Chaos 1 +nette 1 +ums 1 +Botschaft 1 +innerhalb 1 +Pfund 1 +dunkel 1 +Regisseur 1 +antun 1 +Senator 1 +Straßen 1 +gewählt 1 +Klo 1 +ziehe 1 +Termin 1 +unschuldig 1 +enttäuscht 1 +Wolf 1 +vertraue 1 +erkannt 1 +liest 1 +schwimmen 1 +stellte 1 +Wachen 1 +ergibt 1 +erlebt 1 +älter 1 +Familien 1 +Gründe 1 +spielte 1 +Wüste 1 +trägst 1 +Alarm 1 +bieten 1 +Flucht 1 +Flotte 1 +konzentrieren 1 +riskieren 1 +schmeckt 1 +befreien 1 +selten 1 +Spiegel 1 +übergeben 1 +lebendig 1 +starten 1 +vermissen 1 +vernichten 1 +angekommen 1 +betrifft 1 +spüren 1 +wahren 1 +durcheinander 1 +Priester 1 +richtiger 1 +Schlacht 1 +begleiten 1 +Gerechtigkeit 1 +Krebs 1 +singt 1 +Truppen 1 +Information 1 +angesehen 1 +Bomben 1 +Bulle 1 +Kuh 1 +Satz 1 +tötete 1 +überzeugen 1 +akzeptieren 1 +arbeitete 1 +Dreh 1 +Kugeln 1 +Lügner 1 +zählen 1 +hungrig 1 +trennen 1 +Zeugen 1 +ebenfalls 1 +mehrere 1 +stört 1 +beinahe 1 +steigen 1 +Stoff 1 +vorn 1 +Films 1 +Müll 1 +stammt 1 +Talent 1 +umzubringen 1 +Fähigkeit 1 +lauter 1 +Spannende 1 +Strafe 1 +Leiter 1 +locker 1 +Westen 1 +angenommen 1 +dahinter 1 +Jonathan 1 +aufnehmen 1 +beruhige 1 +Feld 1 +verlor 1 +zuhören 1 +Kollegen 1 +schwarz 1 +verhaften 1 +voraus 1 +gleichzeitig 1 +versuchst 1 +Universum 1 +begegnet 1 +draussen 1 +saß 1 +geholt 1 +gucken 1 +Brille 1 +Marian 1 +Notfall 1 +wohne 1 +Pizza 1 +Spuren 1 +brennt 1 +Dämon 1 +leichter 1 +Mitglied 1 +Quelle 1 +begraben 1 +Details 1 +erwarte 1 +fehlen 1 +herausgefunden 1 +geöffnet 1 +Brandenburg 1 +verbracht 1 +Klinik 1 +steigt 1 +auseinander 1 +kochen 1 +Sack 1 +Kohle 1 +Nathan 1 +Rock 1 +soll’s 1 +angreifen 1 +Beerdigung 1 +Bravo 1 +ebenso 1 +Presse 1 +Sonntag 1 +bestellt 1 +Japan 1 +legt 1 +Untersuchung 1 +dicht 1 +Süden 1 +gefolgt 1 +besiegen 1 +dürfte 1 +Eindruck 1 +eurem 1 +leise 1 +Manager 1 +ruiniert 1 +Stärke 1 +Treppe 1 +TV 1 +deutlich 1 +Gift 1 +Militär 1 +trinke 1 +Bye 1 +klingelt 1 +Dinger 1 +Schädel 1 +wichtige 1 +dienen 1 +Hintergrund 1 +Vorschlag 1 +rechtzeitig 1 +Montag 1 +drehten 1 +Brot 1 +Dunkelheit 1 +rausfinden 1 +untersuchen 1 +wartest 1 +Führer 1 +Kommando 1 +wundervoll 1 +vorgestellt 1 +Maschinen 1 +nahmen 1 +nenne 1 +erfüllt 1 +Geschmack 1 +Inspektor 1 +Wetter 1 +Mittagessen 1 +Post 1 +gezwungen 1 +harte 1 +Schlafzimmer 1 +überlegt 1 +beobachten 1 +dauernd 1 +Landes 1 +sitze 1 +gelegt 1 +Schweine 1 +tollen 1 +überprüft 1 +China 1 +ergeben 1 +kämpft 1 +Personen 1 +wechseln 1 +zurückkehren 1 +frisch 1 +gelebt 1 +hergekommen 1 +perfekte 1 +Ritter 1 +Schiffe 1 +Wache 1 +Zahl 1 +Admiral 1 +Lippen 1 +roten 1 +Samstag 1 +Klamotten 1 +öfter 1 +schnappen 1 +wirken 1 +erlauben 1 +Geiseln 1 +Kleidung 1 +Pech 1 +Realität 1 +Tim 1 +Bereich 1 +haben’s 1 +Russen 1 +anhalten 1 +aufstehen 1 +dumme 1 +Figuren 1 +geführt 1 +Staat 1 +Beruf 1 +Patient 1 +Schnee 1 +streiten 1 +zog 1 +Tests 1 +irgendeine 1 +aufgegeben 1 +interessieren 1 +Regel 1 +zuviel 1 +atmen 1 +Gegenteil 1 +Heimat 1 +neulich 1 +schlug 1 +beschlossen 1 +Entscheidungen 1 +erleben 1 +fähig 1 +lebte 1 +Vampir 1 +drücken 1 +geworfen 1 +Mitleid 1 +Prozess 1 +jedoch 1 +ließen 1 +befinden 1 +Gerät 1 +Osten 1 +eifersüchtig 1 +esse 1 +Unterhaltung 1 +Wege 1 +genießen 1 +lebend 1 +Tanz 1 +beeindruckt 1 +rote 1 +Cousin 1 +Geräusch 1 +Hosen 1 +Stil 1 +verlangen 1 +wehtun 1 +wird’s 1 +Station 1 +zusehen 1 +schreibe 1 +anderem 1 +betrogen 1 +aufregend 1 +Beweg 1 +erfolgreich 1 +nannte 1 +Reaktion 1 +glaubte 1 +feuern 1 +heilen 1 +Linie 1 +setze 1 +Teams 1 +Tempel 1 +Geburt 1 +Liebes 1 +erfunden 1 +Schulden 1 +unterschreiben 1 +dritte 1 +Internet 1 +Planet 1 +Sterne 1 +dieselbe 1 +Möglichkeiten 1 +Anweisungen 1 +Cent 1 +Gedanke 1 +Häuser 1 +Locke 1 +Trink 1 +Crew 1 +ist’s 1 +raten 1 +Schulter 1 +Theorie 1 +Fass 1 +Himmels 1 +jederzeit 1 +Politik 1 +Einstellung 1 +Magen 1 +Schrank 1 +gemerkt 1 +Griff 1 +rauskommen 1 +Vögel 1 +Zigaretten 1 +Knopf 1 +Artikel 1 +Ratte 1 +Tunnel 1 +einziges 1 +aufgefallen 1 +behauptet 1 +Frankreich 1 +gaben 1 +gewisse 1 +nieder 1 +Schwestern 1 +Verstärkung 1 +warnen 1 +Babys 1 +erstmal 1 +gewarnt 1 +Versteck 1 +einziger 1 +verschiedenen 1 +altes 1 +Krankenwagen 1 +Mittag 1 +begonnen 1 +besseren 1 +beten 1 +Drama 1 +Fische 1 +Motor 1 +Sturm 1 +trug 1 +West 1 +meins 1 +rechten 1 +Teile 1 +Titel 1 +treibt 1 +besucht 1 +Decke 1 +geträumt 1 +loswerden 1 +sieht’s 1 +unterstützen 1 +vieles 1 +Grunde 1 +dritten 1 +gefühlt 1 +Chancen 1 +empfangen 1 +Schild 1 +Stücke 1 +fange 1 +Zylonen 1 +anfassen 1 +Aussage 1 +bestätigt 1 +erfreut 1 +Holz 1 +packen 1 +reisen 1 +vernünftig 1 +greifen 1 +Leck 1 +Studio 1 +Angelegenheit 1 +Helft 1 +richten 1 +Scheck 1 +Stimmung 1 +Zweck 1 +erschreckt 1 +angezogen 1 +führte 1 +probieren 1 +verteidigen 1 +Vertrau 1 +Zucker 1 +Zuschauer 1 +gelandet 1 +hiermit 1 +Sender 1 +Besitz 1 +spüre 1 +verarschen 1 +Wut 1 +geheim 1 +Kaiser 1 +anbieten 1 +Bürger 1 +fühl 1 +normale 1 +Einheiten 1 +praktisch 1 +verdammtes 1 +beigebracht 1 +geküsst 1 +Kindheit 1 +Mantel 1 +Sequenz 1 +Spieler 1 +wiederhole 1 +Fälle 1 +Käse 1 +Scheißkerl 1 +Verabredung 1 +Gedächtnis 1 +schneiden 1 +Ei 1 +Freundinnen 1 +herkommen 1 +lügt 1 +beantworten 1 +Dämonen 1 +gemeldet 1 +informiert 1 +real 1 +erschaffen 1 +gesteckt 1 +hauen 1 +Stern 1 +Suppe 1 +Zigarette 1 +bewahren 1 +schlechtes 1 +anziehen 1 +Hass 1 +Fabrik 1 +Hinweis 1 +hinterher 1 +Todd 1 +unheimlich 1 +Höhle 1 +Kameras 1 +Krieger 1 +Schreibtisch 1 +schuldest 1 +definitiv 1 +entfernen 1 +geschenkt 1 +Verlust 1 +wiederholen 1 +daneben 1 +Sand 1 +sprachen 1 +steckst 1 +gekämpft 1 +Künstler 1 +Uniform 1 +zurecht 1 +besitzt 1 +Drachen 1 +Schritte 1 +Spitze 1 +überlassen 1 +Anrufe 1 +Fluch 1 +schulde 1 +schwere 1 +würd 1 +erfüllen 1 +solch 1 +trinkt 1 +Bedrohung 1 +lügst 1 +durchgemacht 1 +schlafe 1 +habs 1 +lernt 1 +sollt 1 +Unrecht 1 +Feigling 1 +wachsen 1 +wild 1 +anstatt 1 +Kreis 1 +Mittel 1 +Winter 1 +Stress 1 +Ursache 1 +zahlt 1 +Amsterdam 1 +reingelegt 1 +versagt 1 +Akten 1 +gewöhnt 1 +bestraft 1 +Champagner 1 +Hauptmann 1 +spazieren 1 +starke 1 +Geruch 1 +Mitternacht 1 +tolles 1 +wenige 1 +hübsche 1 +Paradies 1 +Abenteuer 1 +ausruhen 1 +schießt 1 +Umgebung 1 +begeistert 1 +behaupten 1 +Papiere 1 +Schokolade 1 +eingesperrt 1 +zwingen 1 +Abmachung 1 +melde 1 +musstest 1 +Anteil 1 +Media 1 +Spass 1 +verliere 1 +Wissenschaft 1 +zahle 1 +brauchten 1 +stinkt 1 +treiben 1 +welch 1 \ No newline at end of file diff --git a/lang/en/wordlist.txt b/lang/en/wordlist.txt index cd0e0b1..527810d 100644 --- a/lang/en/wordlist.txt +++ b/lang/en/wordlist.txt @@ -1,2000 +1,3052 @@ -the -of -and -to -in -a -is -isn’t -was -that -for -as -with -by -on -are -from -be -or -his -were -it -an -at -not -which -have -haven’t -he -had -hadn’t -this -has -also -their -but -one -can -can’t -its -on the -other -been -more -they -used -first -all -two -citation -than -into -would -only -time -who -most -may -such -some -many -when -after -between -over -these -her -about -there -use -no -them -new -him -will -out -during -made -both -then -often -so -any -being -such as -where -number -could -main -through -system -people -known -each -while -if -called -convert -same -later -three -because -well -work -before -the same -under -part -very -different -became -year -did -didn’t -large -example -several -city -early -until -much -government -found -own -since -she -even -form -power -do -those -around -state -including -set -high -life -against -second -century -within -world -still -end -using -small -name -what -now -usually -American -without -however -began -like -as well -area -make -common -the most -water -United States -another -way -due -must -long -less -four -death -said -film -order -due to -back -public -does -left -based -few -become -known as -given -country -major -British -place -group -considered -among -game -point -used to -period -support -war -music -down -million -important -systems -control -should -took -day -family -language -last -original -result -political -line -members -case -as well as -see -single -just -process -along -similar -take -following -we -although -countries -right -either -times -areas -published -the other -local -include -population -never -data -home -every -various -the time -modern -further -development -per -how -led -possible -military -popular -term -though -history -generally -you -off -rather -men -law -developed -German -held -human -production -body -general -the world -light -sometimes -states -late -field -based on -having -came -above -available -book -others -York -next -created -U.S. -show -himself -out of -wrote -days -died -word -play -again -great -service -age -seen -children -level -released -works -continued -pp. -the two -five -higher -species -energy -required -change -means -team -January -information -theory -New York -produced -making -built -design -role -addition -included -almost -side -position -groups -able -de -land -total -range -July -national -space -written -social -version -Europe -season -force -air -allowed -largest -good -type -itself -received -women -low -throughout -taken -standard -little -least -that is -free -cases -size -thus -school -especially -old -upon -particular -terms -effect -provide -lower -certain -together -present -always -short -parts -words -third -April -described -too -up to -established -might -played -forces -natural -June -once -months -rate -European -numbers -six -man -rather than -hand -typically -value -England -London -October -could be -final -the country -September -average -France -instead -current -December -international -program -character -increased -a few -surface -across -thought -company -followed -best -provided -economic -games -significant -named -function -building -went -return -uses -fact -study -below -full -source -lost -America -person -a number -changes -longer -research -individual -languages -strong -structure -party -larger -run -open -cause -aircraft -away -far -region -need -food -forms -increase -outside -started -material -cannot -November -half -head -market -near -record -traditional -special -style -all the -sent -story -February -player -designed -top -at least -themselves -model -returned -band -because of -help -types -come -points -added -events -network -limited -services -nature -army -former -father -close -view -allow -won -specific -elements -practice -lines -gave -pressure -introduced -produce -moved -whether -religious -put -official -movement -my -Germany -students -trade -method -attack -in order -problems -art -eventually -evidence -referred -results -caused -remained -influence -success -meaning -brought -believed -enough -features -give -young -white -culture -problem -characters -lead -action -according -referred to -conditions -performance -according to -effects -amount -black -business -working -better -difficult -temperature -education -real -money -smaller -create -located -directly -sound -leading -ground -therefore -get -private -formed -particularly -Chinese -television -subject -south -cities -album -class -industry -computer -beginning -community -already -complete -go -players -associated -related -physical -our -town -move -complex -interest -rule -speed -commonly -in order to -rights -living -for example -greater -writing -find -growth -killed -court -levels -house -against the -office -done -shows -Spanish -needed -saw -central -entire -fire -son -books -mostly -access -soon -today -earlier -variety -additional -percent -foreign -ever -recorded -future -widely -title -all of -shown -successful -radio -project -construction -changed -king -remains -mass -personal -policy -code -includes -involved -Africa -idea -names -separate -base -length -rules -key -units -likely -makes -release -cost -church -films -majority -primary -performed -methods -stated -appear -whole -so that -member -allows -decided -Japanese -reported -sold -capital -science -society -rest -stage -event -whose -recent -color -legal -highly -career -song -frequently -placed -simple -defined -me -mother -appeared -India -schools -turn -simply -relationship -multiple -nearly -sense -past -relatively -forced -software -attempt -north -William -quickly -companies -programs -products -direct -site -sources -previous -technology -battle -front -provides -necessary -served -originally -along with -approach -image -seven -experience -Greek -parts of -text -towards -completed -memory -ability -commercial -UK -health -replaced -worked -yet -start -economy -highest -property -behind -knowledge -test -response -largely -sea -reached -president -wide -night -operations -supported -act -training -reason -Britain -list -issues -occur -loss -the government -reduced -active -functions -remain -lack -to do -clear -gas -island -cells -taking -contains -takes -approximately -exist -basis -distance -independent -intended -elected -product -adopted -actually -wife -ended -born -Christian -keep -potential -course -matter -love -issue -objects -things -heavy -oil -center -regular -George -the following -divided -eight -date -James -summer -direction -laws -numerous -account -income -individuals -concept -plan -announced -Russian -failed -applied -values -northern -proposed -ships -resulting -estimated -appears -continue -Canada -red -married -object -studies -media -engine -passed -Henry -Australia -machine -quality -founded -hold -say -parties -letter -Japan -expected -treatment -normal -signal -blood -financial -status -video -older -carried -compared -know -wanted -accepted -via -read -each other -types of -animals -turned -unit -feature -southern -increasing -met -prevent -songs -applications -require -opened -marriage -contain -equipment -playing -section -remaining -properties -asked -the case -ten -Charles -activity -whom -ways -buildings -basic -probably -running -materials -stories -edition -location -noted -cell -analysis -Russia -Rome -removed -becomes -paper -activities -billion -fall -child -cut -operating -degree -ship -occurs -less than -novel -completely -except -inside -Robert -troops -Jewish -report -call -flow -metal -appointed -offered -told -frequency -initial -campaign -discovered -definition -historical -models -allowing -operation -police -poor -weight -not have -ancient -here -primarily -agreed -hit -west -heat -security -David -claimed -positive -easily -environment -scientific -going -regions -ISBN -ball -tradition -reach -requires -St. -extended -charge -female -face -serve -equal -Italy -letters -refused -immediately -believe -supply -effective -internal -versions -cultural -leave -th -mainly -negative -soldiers -question -spread -suggested -Paul -difference -unique -fully -musical -relations -civil -cover -something -board -becoming -famous -hard -plants -situation -workers -Indian -shot -medical -instead of -risk -device -decision -techniques -weeks -previously -western -phase -responsible -purpose -race -article -gives -reference -giving -authority -critical -sets -Italian -in the world -ideas -California -growing -element -instance -kept -moving -rock -share -nuclear -causes -reduce -vote -efforts -table -output -car -determined -win -era -notes -finally -ordered -application -develop -distribution -represented -leaving -quite -the population -woman -devices -contrast -offer -tried -presence -the top -mean -scale -us -river -collection -content -attempt to -says -powerful -comes -the best -humans -travel -resulted -proved -African -combined -road -felt -page -receive -tax -address -teams -connected -pay -annual -despite -disease -birth -closed -kind -agreement -actual -price -claim -goal -management -flight -rise -fuel -brother -maximum -month -presented -write -east -male -gold -reaction -powers -resources -latter -plant -middle -motion -observed -rates -your -currently -station -appearance -volume -avoid -consists -deal -families -argued -room -scene -user -depending -advantage -weapons -target -prior -records -temperatures -speech -spent -focus -mission -dead -constant -existence -lived -daughter -signed -relative -blue -occurred -helped -damage -meant -actions -professional -friends -contact -places -solution -Asia -beyond -meeting -structures -standards -care -exchange -peace -sequence -carry -county -nor -organization -chemical -showed -factors -federal -transport -capacity -Washington -e -in that -meet -be used to -claims -upper -pass -perform -attacks -extremely -effort -the public -opening -tend -initially -literature -alone -attention -paid -sides -useful -centuries -fixed -follow -winter -Thomas -crew -origin -serious -alternative -differences -featured -responsible for -centre -pieces -strength -measure -examples -track -plans -officers -shape -star -Richard -joined -refer -command -existing -cards -leaders -figure -plays -Louis -piece -houses -classes -conflict -fields -stars -th century -composed -positions -vary -round -gain -tend to -seeAlso -impact -slightly -behavior -depending on -look -as to -nothing -raised -hands -compared to -reasons -toward -choice -heart -identified -creating -maintain -refers -condition -constructed -overall -reading -marked -fourth -division -failure -saying -tour -recently -formal -unable -have to -needs -mind -stations -digital -religion -Spain -think -grew -represent -typical -parents -display -competition -acid -and so -fell -week -determine -protection -wave -users -build -processes -projects -return to -technique -to keep -fish -cast -drive -starting -unable to -expanded -affected -entirely -combination -forward -resistance -entered -factor -equivalent -attempts -episode -attempted -away from -providing -sector -external -increases -university -ice -ones -achieved -decades -expressed -Americans -double -demand -regarded -refer to -architecture -audience -issued -fight -citizens -match -in all -aid -limit -sexual -goods -seems -costs -distinct -granted -perhaps -improved -green -file -need to -on to -importance -electric -significantly -DNA -cycle -normally -components -describes -creation -TV -layer -tree -bring -fighting -stop -and also -core -format -increasingly -thousands -animal -leader -understanding -break -unknown -classical -extensive -sign -Israel -the people -host -causing -destroyed -protect -describe -Peter -arrived -square -voice -industrial -purposes -magnetic -containing -finished -follows -nation -rare -cars -continues -that time -planned -radiation -recording -advanced -engines -principle -student -islands -philosophy -sought -officials -rapidly -recognized -secondary -particles -pattern -staff -covered -runs -traffic -awarded -lives -deep -the whole -United Kingdom -expansion -identity -context -controlled -images -organizations -derived -Ireland -administration -bodies -opposed -friend -chosen -greatly -and not -designs -nations -surrounding -urban -zero -otherwise -error -native -setting -gained -why -territory -acts -artists -communication -launched -J. -regional -card -freedom -coast -visible -begin -contract -arms -showing -the past -message -generation -involved in -Mary -nine -leaves -stone -ran -global -popularity -broadcast -minor -sites -instruments -improve -wall -contemporary -soil -Edward -carbon -conducted -symbol -as one -at the end -operate -somewhat -be seen -close to -news -communities -developing -influenced -domestic -port -machines -figures -views -got -declared -note -search -contained -employed -input -step -lead to -bridge -respect -worldwide -caused by -the idea -captured -want -maintained -route -achieve -publication -consider -magazine -orders -winning -formation -Australian -ring -reports -onto -subsequent -details -safety -storage -plane -climate -World War II -accept -connection -the field -dark -managed -practical -shared -be found -measures -visit -capable -movie -spring -calls -independence -obtained -aspects -dance -learning -opposition -begins -towns -split -brain -interest in -Jews -at all -technical -directed -introduction -belief -border -owned -prior to -Chicago -doing -involves -suffered -transfer -offers -enemy -correct -possibly -oxygen -greatest -exists -measured -closely -policies -dedicated -press -producing -scholars -steel -heard -accounts -usage -computers -daily -walls -Canadian -fiction -rejected -stable -score -engineering -ensure -indicate -mentioned -eastern -forms of -path -skin -proper -trial -oldest -statement -mixed -most important -officially -portion -reality -the book -dry -heavily -height -authors -fast -hundred -specifically -author -kind of -block -channel -string -Mexico -Jesus -institutions -instrument -residents -combat -selected -vehicles -coming -manner -rarely -Irish -goes -hot -pair -royal -neither -organized -effectively -expensive -really -suggests -processing -opposite -severe -operated -depends -characteristics -regarding -yards -enter -interests -glass -college -a high -practices -testing -bands -the story -trees -ends -facilities -place in -faith -grow -slow -armed -churches -mode -background -job -writers -defeated -cable -leadership -transmission -patterns -thing -Dutch -density -attached -big -bottom -atmosphere -networks -degrees -dropped -electronic -i -might be -strongly -items -roles -translation -pages -apply -assigned -signals -extent -viewed -Michael -debate -minimum -periods -programming -visited -ultimately -frame -hardware -prices -whereas -as a result -establish -permanent -holds -strategy -escape -authorities -acting -join -listed -sex -edge -screen -decade -critics -cold -continuous -generated -solid -violence -liquid -iron -investment -likely to -sufficient -composition -broke -experienced -et -exactly -map -ratio -documents -anything -questions -twice -North America -converted -elections -slaves -threat -time in -studio -contributed -household -wind -train -truth -changing -for each -rural -capable of -sales -secret -bus -football -movements -theories -labor -modified -Christ -cross -expression -stock -atoms -chain -earth -leads -understand -box -impossible -principles -requirements -weather -considerable -director -earliest -vehicle -added to -moral -Johnson -district -literary -respectively -am -set up -prepared -subsequently -preferred -trying -sports -mechanism -concerned -courts -leading to -task -treated -voltage -translated -hydrogen -inspired -victory -advance -appropriate -faster -nearby -theme -to go -drawn -standing -arts -parallel -adding -component -relationships -waves -focused -kill -locations -notable -printed -separated -resolution -tools -officer -subject to -the amount -essential -medium -opportunity -solar -etc. -teaching -argument -don’t -Columbia -GDP -cultures -painting -tests -bit -amounts -molecules -roads -launch -crime -fleet -holding -learned -stay -stored -pure -balance -and all -successfully -picture -politics -circuit -request -Joseph -environmental -distributed -produces -affect -prime -revealed -eye -universe -fans -patients -wood -governments -Berlin -occasionally -selection -taught -acquired -fundamental -linear -Alexander -choose -episodes -floor -angle -incorporated -try -benefit -description -die -studied -Poland -dates -list of -artist -flat -protein -concluded -hundreds -speak -extra -subjects -approved -electrons -chance -easy -implemented -husband -ideal -planning -concepts -grown -understood -extreme -intelligence -quantum -receiving -Scotland -online -knew -unless -Jones -out to -rapid -shall -Texas -academic -reputation -the future -experiments -explained -park -eyes -sections -audio -in fact -persons -store -budget -corresponding -sister -charged -broken -capture -script -rich \ No newline at end of file +Word Count +the 2814 +of 1698 +and 1497 +to 1298 +in 871 +I 588 +that 554 +was 504 +his 440 +he 420 +it 403 +with 386 +is 378 +for 355 +as 352 +had 307 +you 302 +not 287 +be 283 +her 260 +on 256 +at 255 +by 253 +which 229 +have 217 +or 211 +from 205 +this 201 +him 199 +but 195 +all 185 +she 171 +they 167 +were 166 +my 164 +are 161 +me 151 +one 142 +their 141 +so 140 +an 132 +said 132 +them 125 +we 125 +who 124 +would 120 +been 118 +will 116 +no 112 +when 99 +there 98 +if 98 +more 95 +out 94 +up 90 +into 85 +do 84 +any 83 +your 83 +what 80 +has 80 +man 79 +could 79 +other 77 +than 75 +our 75 +some 74 +very 73 +time 72 +upon 71 +about 71 +may 70 +its 69 +only 66 +now 66 +like 64 +little 64 +then 63 +can 61 +should 60 +made 59 +did 59 +us 59 +such 57 +a 57 +great 56 +before 56 +must 55 +two 55 +these 55 +see 54 +know 54 +over 53 +much 51 +down 49 +after 49 +first 49 +Mr 49 +good 48 +men 46 +own 46 +never 45 +most 44 +old 44 +shall 44 +day 44 +where 44 +those 44 +came 44 +come 44 +himself 43 +way 43 +work 41 +life 41 +without 41 +go 41 +make 40 +well 40 +through 40 +being 40 +long 40 +say 39 +might 39 +how 39 +am 38 +too 38 +even 38 +def 37 +again 37 +many 37 +back 37 +here 36 +think 36 +every 35 +people 35 +went 35 +same 34 +last 34 +thought 34 +away 34 +under 34 +take 33 +found 33 +hand 32 +eyes 32 +still 32 +place 31 +while 31 +just 31 +also 30 +young 30 +yet 29 +though 29 +against 28 +things 28 +get 28 +ever 28 +give 28 +god 28 +years 27 +off 27 +face 27 +nothing 27 +right 27 +once 27 +another 27 +left 27 +part 26 +saw 26 +house 26 +world 26 +head 26 +three 25 +took 25 +new 25 +love 25 +always 25 +Mrs 25 +put 25 +night 24 +each 24 +king 24 +between 24 +tell 24 +mind 24 +heart 23 +few 23 +because 23 +thing 23 +whom 23 +far 23 +seemed 22 +looked 22 +called 22 +whole 22 +de 22 +set 22 +both 22 +got 22 +find 22 +done 22 +heard 21 +look 21 +name 21 +days 21 +told 21 +let 21 +lord 21 +country 21 +asked 21 +going 21 +seen 21 +better 21 +having 21 +home 21 +knew 21 +side 20 +something 20 +moment 20 +father 19 +among 19 +course 19 +hands 19 +woman 19 +enough 19 +words 19 +mother 19 +soon 19 +full 19 +end 18 +gave 18 +room 18 +almost 18 +small 18 +thou 18 +cannot 18 +water 18 +want 18 +however 18 +light 18 +quite 18 +brought 17 +nor 17 +word 17 +whose 17 +given 17 +door 17 +best 17 +turned 17 +taken 17 +does 17 +use 17 +morning 17 +myself 16 +felt 16 +until 16 +since 16 +power 16 +themselves 16 +used 16 +rather 16 +began 16 +present 16 +voice 16 +others 16 +white 16 +works 16 +less 16 +money 16 +next 16 +poor 16 +death 15 +stood 15 +form 15 +within 15 +together 15 +till 15 +thy 15 +large 15 +matter 15 +kind 15 +often 15 +certain 15 +herself 15 +year 15 +friend 15 +half 15 +order 15 +round 15 +TRUE 15 +anything 14 +keep 14 +sent 14 +wife 14 +means 14 +believe 14 +passed 14 +feet 14 +near 14 +public 14 +state 14 +son 14 +hundred 14 +children 14 +thus 14 +hope 14 +alone 14 +above 14 +case 14 +dear 14 +thee 13 +says 13 +person 13 +high 13 +read 13 +city 13 +already 13 +received 13 +fact 13 +gone 13 +girl 13 +known 13 +hear 13 +times 13 +least 13 +perhaps 13 +sure 13 +indeed 13 +english 13 +open 13 +body 13 +itself 13 +along 13 +land 12 +return 12 +leave 12 +air 12 +nature 12 +answered 12 +either 12 +law 12 +help 12 +lay 12 +point 12 +child 12 +letter 12 +four 12 +wish 12 +fire 12 +cried 12 +women 12 +speak 12 +number 12 +therefore 12 +hour 12 +friends 12 +held 12 +free 12 +war 12 +during 12 +several 12 +business 12 +whether 12 +er 12 +manner 12 +second 12 +reason 11 +replied 11 +united 11 +call 11 +general 11 +why 11 +behind 11 +became 11 +john 11 +become 11 +dead 11 +earth 11 +boy 11 +lost 11 +forth 11 +thousand 11 +looking 11 +I’ll 11 +family 11 +soul 11 +feel 11 +coming 11 +England 11 +spirit 11 +question 11 +care 11 +truth 11 +ground 11 +really 11 +rest 11 +mean 11 +different 11 +making 11 +possible 10 +fell 10 +towards 10 +human 10 +kept 10 +short 10 +town 10 +following 10 +need 10 +cause 10 +met 10 +evening 10 +returned 10 +five 10 +strong 10 +able 10 +french 10 +live 10 +lady 10 +subject 10 +sn 10 +answer 10 +sea 10 +fear 10 +understand 10 +hard 10 +terms 10 +doubt 10 +around 10 +ask 10 +arms 10 +turn 10 +sense 10 +seems 10 +black 10 +bring 10 +followed 10 +beautiful 10 +close 9 +dark 9 +hold 9 +character 9 +sort 9 +sight 9 +ten 9 +show 9 +party 9 +fine 9 +ye 9 +ready 9 +story 9 +common 9 +book 9 +electronic 9 +talk 9 +account 9 +mark 9 +interest 9 +written 9 +can’t 9 +bed 9 +necessary 9 +age 9 +else 9 +force 9 +idea 9 +longer 9 +art 9 +spoke 9 +across 9 +brother 9 +early 9 +ought 9 +sometimes 9 +line 9 +saying 9 +table 9 +appeared 8 +river 8 +continued 8 +eye 8 +ety 8 +sun 8 +information 8 +later 8 +everything 8 +reached 8 +suddenly 8 +past 8 +hours 8 +strange 8 +deep 8 +change 8 +miles 8 +feeling 8 +act 8 +meet 8 +paid 8 +further 8 +purpose 8 +happy 8 +added 8 +seem 8 +taking 8 +blood 8 +rose 8 +south 8 +beyond 8 +cold 8 +neither 8 +forward 8 +view 8 +I’ve 8 +position 8 +sound 8 +none 8 +entered 8 +clear 8 +road 8 +late 8 +stand 8 +suppose 8 +la 8 +daughter 8 +real 8 +nearly 8 +mine 8 +laws 8 +knowledge 8 +comes 8 +toward 8 +bad 8 +cut 8 +copy 8 +husband 8 +six 8 +France 8 +living 8 +peace 8 +didn’t 7 +low 7 +north 7 +remember 7 +effect 7 +natural 7 +pretty 7 +fall 7 +fair 7 +service 7 +below 7 +except 7 +American 7 +hair 7 +London 7 +laid 7 +pass 7 +led 7 +copyright 7 +doing 7 +army 7 +run 7 +horse 7 +future 7 +opened 7 +pleasure 7 +history 7 +west 7 +pay 7 +red 7 +an’ 7 +hath 7 +note 7 +although 7 +wanted 7 +gold 7 +makes 7 +desire 7 +play 7 +master 7 +office 7 +tried 7 +front 7 +big 7 +lived 7 +certainly 7 +wind 7 +receive 7 +attention 7 +government 7 +unto 7 +church 7 +strength 7 +length 7 +company 7 +placed 7 +paper 7 +letters 7 +probably 7 +glad 7 +important 7 +especially 7 +greater 7 +yourself 7 +fellow 7 +bear 7 +opinion 7 +window 7 +ran 7 +faith 7 +ago 7 +agreement 7 +charge 6 +beauty 6 +lips 6 +remained 6 +arm 6 +latter 6 +duty 6 +send 6 +distance 6 +silence 6 +foot 6 +wild 6 +object 6 +die 6 +save 6 +gentleman 6 +trees 6 +green 6 +trouble 6 +smile 6 +books 6 +wrong 6 +various 6 +sleep 6 +persons 6 +blockquote 6 +happened 6 +particular 6 +drew 6 +minutes 6 +hardly 6 +walked 6 +chief 6 +chance 6 +according 6 +beginning 6 +action 6 +deal 6 +loved 6 +visit 6 +thinking 6 +follow 6 +standing 6 +knows 6 +try 6 +presence 6 +heavy 6 +sweet 6 +plain 6 +donations 6 +immediately 6 +wrote 6 +mouth 6 +rich 6 +thoughts 6 +months 6 +won’t 6 +afraid 6 +Paris 6 +single 6 +joy 6 +enemy 6 +broken 6 +unless 6 +states 6 +ship 6 +condition 6 +carry 6 +exclaimed 6 +including 6 +filled 6 +seeing 6 +influence 6 +write 6 +boys 6 +appear 6 +outside 6 +secret 6 +parts 6 +please 6 +appearance 6 +evil 6 +march 6 +whatever 6 +slowly 6 +tears 6 +horses 6 +places 6 +caught 6 +stay 5 +instead 5 +struck 5 +blue 5 +York 5 +impossible 5 +period 5 +sister 5 +battle 5 +school 5 +raised 5 +occasion 5 +married 5 +man’s 5 +former 5 +food 5 +youth 5 +learned 5 +merely 5 +reach 5 +system 5 +twenty 5 +dinner 5 +quiet 5 +easily 5 +moved 5 +afterwards 5 +giving 5 +walk 5 +stopped 5 +laughed 5 +language 5 +expression 5 +week 5 +hall 5 +danger 5 +property 5 +wonder 5 +usual 5 +figure 5 +born 5 +court 5 +generally 5 +grew 5 +showed 5 +getting 5 +ancient 5 +respect 5 +third 5 +worth 5 +simple 5 +tree 5 +leaving 5 +remain 5 +society 5 +fight 5 +wall 5 +result 5 +heaven 5 +started 5 +command 5 +tone 5 +regard 5 +expected 5 +mere 5 +month 5 +beside 5 +silent 5 +perfect 5 +experience 5 +street 5 +writing 5 +goes 5 +circumstances 5 +entirely 5 +fresh 5 +duke 5 +covered 5 +bound 5 +east 5 +wood 5 +stone 5 +quickly 5 +notice 5 +bright 5 +boat 5 +noble 5 +meant 5 +somewhat 5 +sudden 5 +value 5 +c. 5 +direction 5 +chair 5 +due 5 +support 5 +tom 5 +date 5 +waiting 5 +village 5 +lives 5 +reading 5 +agree 5 +lines 5 +considered 5 +field 5 +observed 5 +scarcely 5 +wished 5 +wait 5 +greatest 5 +permission 5 +success 5 +piece 5 +British 5 +ex 5 +formed 5 +speaking 5 +trying 5 +conversation 5 +proper 5 +hill 5 +music 5 +opportunity 5 +that’s 5 +German 5 +afternoon 5 +cry 5 +cost 5 +allowed 5 +girls 5 +considerable 5 +broke 5 +honour 5 +seven 5 +private 5 +sit 5 +news 5 +top 5 +scene 5 +discovered 5 +marriage 5 +step 5 +garden 5 +race 5 +begin 5 +per 5 +individual 5 +sitting 5 +learn 5 +political 5 +difficult 5 +bit 5 +speech 5 +lie 4 +cast 4 +eat 4 +authority 4 +etc. 4 +floor 4 +ill 4 +ways 4 +officers 4 +offered 4 +original 4 +happiness 4 +flowers 4 +produced 4 +summer 4 +provide 4 +study 4 +religion 4 +picture 4 +walls 4 +personal 4 +America 4 +watch 4 +pleased 4 +leaves 4 +declared 4 +hot 4 +understood 4 +effort 4 +prepared 4 +escape 4 +attempt 4 +supposed 4 +killed 4 +fast 4 +author 4 +Indian 4 +brown 4 +determined 4 +pain 4 +spring 4 +takes 4 +drawn 4 +soldiers 4 +houses 4 +beneath 4 +talking 4 +turning 4 +century 4 +steps 4 +intended 4 +soft 4 +straight 4 +matters 4 +likely 4 +corner 4 +trademark 4 +justice 4 +simply 4 +produce 4 +trust 4 +appears 4 +Rome 4 +laugh 4 +forget 4 +Europe 4 +passage 4 +eight 4 +closed 4 +ourselves 4 +gives 4 +dress 4 +passing 4 +terrible 4 +required 4 +medium 4 +efforts 4 +sake 4 +breath 4 +wise 4 +ladies 4 +possession 4 +pleasant 4 +perfectly 4 +o’ 4 +memory 4 +usually 4 +grave 4 +fixed 4 +modern 4 +spot 4 +troops 4 +rise 4 +break 4 +fifty 4 +island 4 +meeting 4 +camp 4 +nation 4 +existence 4 +reply 4 +I’d 4 +copies 4 +sky 4 +touch 4 +equal 4 +fortune 4 +v. 4 +shore 4 +domain 4 +named 4 +situation 4 +looks 4 +promise 4 +orders 4 +degree 4 +middle 4 +winter 4 +plan 4 +spent 4 +allow 4 +pale 4 +conduct 4 +running 4 +religious 4 +surprise 4 +minute 4 +roman 4 +cases 4 +shot 4 +lead 4 +move 4 +names 4 +stop 4 +higher 4 +et 4 +father’s 4 +threw 4 +worse 4 +built 4 +spoken 4 +glass 4 +board 4 +vain 4 +affairs 4 +instance 4 +safe 4 +loss 4 +doctor 4 +offer 4 +class 4 +complete 4 +access 4 +lower 4 +wouldn’t 4 +repeated 4 +forms 4 +darkness 4 +military 4 +warm 4 +drink 4 +passion 4 +ones 4 +physical 4 +example 4 +ears 4 +questions 4 +start 4 +lying 4 +smiled 4 +keeping 4 +spite 4 +shown 4 +directly 4 +james 4 +hart 4 +serious 4 +hat 4 +dog 4 +silver 4 +sufficient 4 +main 4 +mentioned 4 +servant 4 +pride 4 +crowd 4 +train 4 +wonderful 4 +moral 4 +instant 4 +associated 4 +path 4 +greek 4 +meaning 4 +fit 4 +ordered 4 +lot 4 +he’s 4 +proved 4 +obliged 4 +enter 4 +rule 4 +sword 4 +attack 4 +seat 4 +game 4 +health 4 +paragraph 4 +statement 4 +social 4 +refund 4 +sorry 4 +courage 4 +members 4 +grace 4 +official 4 +dream 4 +worthy 4 +rock 4 +jack 4 +provided 4 +special 4 +shook 4 +request 4 +mighty 4 +glance 4 +heads 4 +movement 4 +fee 4 +share 4 +expect 4 +couldn’t 4 +dollars 4 +spread 4 +opposite 4 +glory 4 +twelve 4 +space 4 +engaged 4 +peter 4 +wine 4 +ordinary 4 +mountains 4 +taste 4 +iron 4 +isn’t 4 +distribute 4 +trade 4 +consider 4 +greatly 4 +accepted 4 +forced 4 +advantage 4 +ideas 4 +decided 4 +using 4 +officer 4 +rate 4 +clothes 4 +sign 4 +feelings 4 +native 4 +promised 4 +judge 4 +difference 4 +working 4 +anxious 4 +marry 4 +captain 4 +finished 4 +extent 4 +watched 4 +curious 4 +foreign 4 +besides 4 +method 4 +excellent 4 +confidence 4 +marked 4 +’em 4 +jesus 4 +exactly 4 +importance 4 +finally 4 +bill 3 +vast 3 +prove 3 +fancy 3 +quick 3 +yes 3 +sought 3 +prevent 3 +neck 3 +hearts 3 +liberty 3 +interesting 3 +sides 3 +legal 3 +gentlemen 3 +dry 3 +serve 3 +aside 3 +pure 3 +concerning 3 +forgotten 3 +lose 3 +powers 3 +possessed 3 +thrown 3 +evidence 3 +distant 3 +progress 3 +similar 3 +narrow 3 +altogether 3 +building 3 +page 3 +particularly 3 +knowing 3 +weeks 3 +settled 3 +holding 3 +mountain 3 +search 3 +sad 3 +sin 3 +lies 3 +proud 3 +pieces 3 +clearly 3 +price 3 +ships 3 +thirty 3 +sick 3 +honest 3 +shut 3 +talked 3 +bank 3 +fate 3 +dropped 3 +judgment 3 +conditions 3 +king’s 3 +accept 3 +hills 3 +removed 3 +forest 3 +measure 3 +species 3 +seek 3 +highest 3 +otherwise 3 +stream 3 +honor 3 +carefully 3 +obtained 3 +ear 3 +bread 3 +bottom 3 +additional 3 +presented 3 +aid 3 +fingers 3 +remembered 3 +choose 3 +agreed 3 +animal 3 +events 3 +there’s 3 +fully 3 +delight 3 +rights 3 +amount 3 +obtain 3 +tax 3 +servants 3 +sons 3 +cross 3 +shoulders 3 +thick 3 +points 3 +stranger 3 +woods 3 +facts 3 +dare 3 +grow 3 +creature 3 +hung 3 +rain 3 +FALSE 3 +tall 3 +gate 3 +nations 3 +created 3 +refused 3 +quietly 3 +surface 3 +freely 3 +holy 3 +streets 3 +blow 3 +july 3 +regarded 3 +fashion 3 +report 3 +coast 3 +daily 3 +file 3 +shoulder 3 +surprised 3 +faces 3 +succeeded 3 +birds 3 +distribution 3 +royal 3 +song 3 +wealth 3 +comfort 3 +failed 3 +freedom 3 +peculiar 3 +anyone 3 +advance 3 +gentle 3 +surely 3 +animals 3 +waited 3 +secure 3 +desired 3 +grass 3 +touched 3 +occupied 3 +draw 3 +stage 3 +portion 3 +expressed 3 +opening 3 +june 3 +spirits 3 +fish 3 +tongue 3 +capital 3 +angry 3 +growing 3 +served 3 +carriage 3 +weather 3 +breast 3 +presently 3 +snow 3 +papers 3 +necessity 3 +practice 3 +claim 3 +hast 3 +education 3 +sharp 3 +prince 3 +permitted 3 +group 3 +enemies 3 +robert 3 +played 3 +throughout 3 +pity 3 +expense 3 +yours 3 +million 3 +add 3 +pray 3 +taught 3 +explained 3 +tired 3 +leading 3 +kill 3 +shadow 3 +companion 3 +weight 3 +mass 3 +established 3 +suffered 3 +gray 3 +brave 3 +thin 3 +satisfied 3 +check 3 +virtue 3 +golden 3 +numerous 3 +frequently 3 +famous 3 +telling 3 +powerful 3 +alive 3 +waters 3 +national 3 +weak 3 +divine 3 +material 3 +principal 3 +gathered 3 +suggested 3 +frank 3 +valley 3 +guess 3 +finding 3 +yellow 3 +heat 3 +remains 3 +bent 3 +seized 3 +guard 3 +equally 3 +naturally 3 +box 3 +remarkable 3 +gods 3 +moon 3 +slight 3 +style 3 +pointed 3 +saved 3 +windows 3 +crossed 3 +louis 3 +pounds 3 +ain’t 3 +evidently 3 +principle 3 +immediate 3 +willing 3 +consequence 3 +richard 3 +principles 3 +characters 3 +paul 3 +season 3 +remarked 3 +science 3 +tender 3 +worked 3 +grown 3 +whispered 3 +interested 3 +quarter 3 +midst 3 +liked 3 +advanced 3 +apparently 3 +bore 3 +pwh 3 +active 3 +noticed 3 +aware 3 +thomas 3 +uncle 3 +list 3 +dangerous 3 +august 3 +calm 3 +genius 3 +sacred 3 +kingdom 3 +entire 3 +popular 3 +unknown 3 +nice 3 +habit 3 +spanish 3 +familiar 3 +reader 3 +published 3 +direct 3 +handsome 3 +you’ll 3 +joined 3 +actually 3 +kings 3 +posted 3 +approach 3 +Washington 3 +hearing 3 +needed 3 +increased 3 +walking 3 +twice 3 +throw 3 +intellectual 3 +appointed 3 +wisdom 3 +ceased 3 +truly 3 +numbers 3 +demanded 3 +priest 3 +wounded 3 +sorrow 3 +drive 3 +fault 3 +listened 3 +palace 3 +affair 3 +contact 3 +distinguished 3 +station 3 +beat 3 +distributed 3 +listen 3 +Italy 3 +fool 3 +becomes 3 +watching 3 +hurt 3 +wants 3 +express 3 +occurred 3 +favour 3 +height 3 +size 3 +edge 3 +subjects 3 +task 3 +follows 3 +interests 3 +nine 3 +sympathy 3 +burst 3 +putting 3 +dressed 3 +lifted 3 +hopes 3 +suffer 3 +noise 3 +smiling 3 +rode 3 +tells 3 +minds 3 +farther 3 +literature 3 +vessel 3 +affection 3 +suffering 3 +proceeded 3 +flesh 3 +advice 3 +grand 3 +carrying 3 +legs 3 +Spain 3 +post 3 +collection 3 +empty 3 +rank 3 +storm 3 +god’s 3 +imagine 3 +wore 3 +duties 3 +admitted 3 +countries 3 +pocket 3 +arrival 3 +imagination 3 +driven 3 +loud 3 +sentence 3 +lovely 3 +extraordinary 3 +November 3 +December 3 +happen 3 +absence 3 +breakfast 3 +population 3 +thank 3 +rules 3 +inhabitants 3 +series 3 +laughing 3 +address 3 +relief 3 +bird 3 +owner 3 +impression 3 +satisfaction 3 +coat 3 +prepare 3 +relations 3 +shape 3 +birth 3 +rapidly 3 +smoke 3 +January 3 +mother’s 3 +machine 3 +content 3 +consideration 3 +accompanied 3 +regular 3 +moving 3 +stands 3 +wholly 3 +teeth 3 +busy 3 +treated 3 +burning 3 +shame 3 +quality 3 +bay 3 +discover 3 +inside 3 +brain 3 +soil 3 +completely 3 +message 3 +ring 3 +resolved 3 +calling 3 +phrase 3 +acts 3 +mention 3 +square 3 +pair 3 +won 3 +title 3 +understanding 3 +Sunday 3 +fruit 3 +mad 3 +forces 3 +included 3 +tea 3 +rocks 3 +nearer 3 +slaves 3 +falling 3 +absolutely 3 +slow 3 +bearing 3 +mercy 3 +larger 3 +explain 3 +contain 3 +grief 3 +soldier 3 +wasn’t 3 +countenance 3 +previous 3 +explanation 3 +welcome 3 +proposed 3 +prayer 3 +stars 3 +Germany 3 +belief 3 +informed 3 +moments 3 +poetry 3 +constant 3 +buy 3 +final 3 +faithful 3 +ride 3 +policy 3 +supper 3 +drawing 3 +excitement 3 +dying 3 +demand 3 +fighting 3 +fields 3 +drove 3 +upper 3 +sum 3 +motion 2 +assistance 2 +forty 2 +April 2 +stones 2 +fees 2 +kindly 2 +dignity 2 +catch 2 +October 2 +seated 2 +knees 2 +amongst 2 +current 2 +sending 2 +parties 2 +objects 2 +gained 2 +bitter 2 +possibly 2 +slave 2 +separate 2 +loose 2 +text 2 +receiving 2 +worst 2 +sold 2 +don 2 +credit 2 +chosen 2 +hoped 2 +printed 2 +terror 2 +features 2 +fond 2 +control 2 +capable 2 +fifteen 2 +doesn’t 2 +firm 2 +superior 2 +cruel 2 +spiritual 2 +splendid 2 +proof 2 +pressed 2 +sooner 2 +join 2 +process 2 +crime 2 +dust 2 +instantly 2 +lands 2 +relation 2 +doors 2 +concerned 2 +deeply 2 +practical 2 +colour 2 +sing 2 +destroy 2 +anger 2 +distributing 2 +results 2 +increase 2 +reasons 2 +nose 2 +friendly 2 +entrance 2 +rooms 2 +admit 2 +supply 2 +clean 2 +useful 2 +yesterday 2 +delicate 2 +fail 2 +continue 2 +remove 2 +addressed 2 +choice 2 +huge 2 +needs 2 +wear 2 +blind 2 +unable 2 +cover 2 +double 2 +victory 2 +dozen 2 +constantly 2 +level 2 +India 2 +release 2 +rough 2 +ended 2 +shows 2 +fly 2 +praise 2 +devil 2 +ahead 2 +smith 2 +connected 2 +degrees 2 +gain 2 +addition 2 +committed 2 +chamber 2 +notes 2 +Italian 2 +gradually 2 +acquaintance 2 +bought 2 +souls 2 +mission 2 +sacrifice 2 +cities 2 +mistake 2 +exercise 2 +conscience 2 +based 2 +car 2 +buried 2 +theory 2 +commanded 2 +nobody 2 +minister 2 +closely 2 +energy 2 +dick 2 +bare 2 +fought 2 +partly 2 +mistress 2 +hate 2 +arose 2 +playing 2 +color 2 +lake 2 +safety 2 +provisions 2 +description 2 +asleep 2 +centre 2 +faint 2 +thinks 2 +parents 2 +escaped 2 +careful 2 +enjoy 2 +drop 2 +brilliant 2 +brief 2 +bringing 2 +worship 2 +goods 2 +tale 2 +skin 2 +roof 2 +grey 2 +highly 2 +crown 2 +castle 2 +excited 2 +throne 2 +stated 2 +despair 2 +ease 2 +attached 2 +total 2 +kindness 2 +mile 2 +citizens 2 +circle 2 +dull 2 +extreme 2 +clouds 2 +figures 2 +intention 2 +prison 2 +term 2 +assured 2 +hidden 2 +thoroughly 2 +cup 2 +member 2 +civil 2 +apply 2 +labor 2 +everywhere 2 +intelligence 2 +strike 2 +fairly 2 +comply 2 +fellows 2 +haven’t 2 +event 2 +gently 2 +connection 2 +protection 2 +conscious 2 +edition 2 +directed 2 +pulled 2 +flight 2 +evident 2 +surrounded 2 +wishes 2 +yards 2 +voices 2 +weary 2 +couple 2 +variety 2 +whilst 2 +volume 2 +details 2 +older 2 +requirements 2 +custom 2 +apart 2 +bow 2 +awful 2 +everybody 2 +labour 2 +asking 2 +lover 2 +showing 2 +introduced 2 +suit 2 +becoming 2 +composed 2 +plans 2 +rendered 2 +pictures 2 +lest 2 +volunteers 2 +singing 2 +eager 2 +precious 2 +paused 2 +require 2 +meat 2 +whenever 2 +milk 2 +dogs 2 +successful 2 +plants 2 +vision 2 +rare 2 +granted 2 +raise 2 +Egypt 2 +manners 2 +cousin 2 +you’ve 2 +development 2 +obs 2 +cool 2 +trial 2 +learning 2 +approached 2 +bridge 2 +abroad 2 +devoted 2 +paying 2 +literary 2 +writer 2 +fn 2 +Israel 2 +disappeared 2 +interrupted 2 +stock 2 +readers 2 +dreadful 2 +female 2 +protect 2 +accustomed 2 +Virginia 2 +type 2 +recognized 2 +salt 2 +destroyed 2 +signs 2 +innocent 2 +temper 2 +plenty 2 +pope 2 +avoid 2 +hurried 2 +represented 2 +favor 2 +mental 2 +attitude 2 +returning 2 +admiration 2 +brothers 2 +anxiety 2 +queen 2 +teach 2 +count 2 +curiosity 2 +solemn 2 +causes 2 +vessels 2 +compelled 2 +dance 2 +hotel 2 +wicked 2 +fled 2 +kissed 2 +guns 2 +fill 2 +visible 2 +younger 2 +guide 2 +earnest 2 +actual 2 +companions 2 +prisoner 2 +miserable 2 +lad 2 +harm 2 +views 2 +Irish 2 +utterly 2 +ends 2 +shop 2 +stairs 2 +pardon 2 +gay 2 +beg 2 +seldom 2 +kinds 2 +record 2 +fat 2 +sand 2 +violent 2 +branches 2 +inquired 2 +September 2 +worn 2 +Ireland 2 +flat 2 +departure 2 +delivered 2 +gift 2 +ruin 2 +skill 2 +cattle 2 +equipment 2 +temple 2 +calls 2 +earlier 2 +license 2 +visited 2 +en 2 +consent 2 +sufficiently 2 +natives 2 +wound 2 +laughter 2 +contained 2 +perceived 2 +scattered 2 +whence 2 +rushed 2 +chiefly 2 +bold 2 +anywhere 2 +witness 2 +foolish 2 +helped 2 +kitchen 2 +sell 2 +anybody 2 +self 2 +extremely 2 +treatment 2 +throat 2 +dreams 2 +patient 2 +speed 2 +growth 2 +quantity 2 +Latin 2 +immense 2 +conclusion 2 +computer 2 +affected 2 +severe 2 +excuse 2 +triumph 2 +origin 2 +slept 2 +eternal 2 +thine 2 +audience 2 +pages 2 +sounds 2 +swift 2 +limited 2 +wings 2 +stepped 2 +services 2 +library 2 +remaining 2 +containing 2 +base 2 +confusion 2 +win 2 +maid 2 +charming 2 +editions 2 +attended 2 +softly 2 +reality 2 +performed 2 +glorious 2 +likewise 2 +site 2 +sail 2 +frightened 2 +acquainted 2 +unhappy 2 +feared 2 +article 2 +prisoners 2 +store 2 +adopted 2 +shalt 2 +remark 2 +cook 2 +thousands 2 +pause 2 +inclined 2 +convinced 2 +band 2 +valuable 2 +hence 2 +desert 2 +effects 2 +kiss 2 +plant 2 +ice 2 +ball 2 +stick 2 +absolute 2 +readily 2 +behold 2 +fierce 2 +argument 2 +observe 2 +blessed 2 +bosom 2 +rage 2 +striking 2 +discovery 2 +creatures 2 +shouted 2 +guilty 2 +related 2 +setting 2 +forgot 2 +punishment 2 +gun 2 +slightly 2 +articles 2 +police 2 +mysterious 2 +extended 2 +confess 2 +shade 2 +murder 2 +emotion 2 +destruction 2 +wondered 2 +increasing 2 +hide 2 +expedition 2 +horror 2 +local 2 +expenses 2 +ignorant 2 +doctrine 2 +generous 2 +range 2 +host 2 +wet 2 +cloud 2 +mystery 2 +waste 2 +changes 2 +possess 2 +consciousness 2 +February 2 +trembling 2 +disease 2 +formerly 2 +spend 2 +production 2 +source 2 +mankind 2 +universal 2 +deck 2 +sees 2 +habits 2 +estate 2 +aunt 2 +reign 2 +humble 2 +compliance 2 +delay 2 +shining 2 +reported 2 +hers 2 +unfortunate 2 +midnight 2 +listening 2 +flower 2 +hero 2 +accomplished 2 +doth 2 +classes 2 +thanks 2 +banks 2 +philosophy 2 +belong 2 +finger 2 +comfortable 2 +market 2 +cap 2 +waves 2 +woman’s 2 +glanced 2 +troubled 2 +difficulties 2 +picked 2 +European 2 +purposes 2 +somewhere 2 +delighted 2 +pushed 2 +press 2 +household 2 +fleet 2 +baby 2 +region 2 +lately 2 +uttered 2 +exact 2 +image 2 +ages 2 +murmured 2 +melancholy 2 +suspicion 2 +bowed 2 +refuse 2 +staff 2 +liability 2 +we’ll 2 +enjoyed 2 +stretched 2 +gaze 2 +belonged 2 +ashamed 2 +reward 2 +meal 2 +blame 2 +nodded 2 +status 2 +opinions 2 +indicate 2 +poem 2 +savage 2 +arise 2 +voyage 2 +misery 2 +guests 2 +painted 2 +attend 2 +afford 2 +donate 2 +job 2 +proceed 2 +loves 2 +forehead 2 +regret 2 +plainly 2 +risk 2 +ad 2 +lighted 2 +angel 2 +rapid 2 +distinct 2 +doubtless 2 +properly 2 +wit 2 +fame 2 +singular 2 +error 2 +utmost 2 +methods 2 +reputation 2 +appeal 2 +she’s 2 +strongly 2 +lack 2 +breaking 2 +dawn 2 +violence 2 +fatal 2 +render 2 +career 2 +design 2 +displayed 2 +gets 2 +commercial 2 +forgive 2 +lights 2 +agreeable 2 +suggestion 2 +utter 2 +sheep 2 +resolution 2 +spare 2 +patience 2 +domestic 2 +concluded 2 +’tis 2 +farm 2 +reference 2 +Chinese 2 +exist 2 +corn 2 +approaching 2 +alike 2 +mounted 2 +issue 2 +key 2 +providing 2 +majority 2 +measures 2 +towns 2 +flame 2 +Boston 2 +dared 2 +ignorance 2 +reduced 2 +occasionally 2 +weakness 2 +furnished 2 +china 2 +priests 2 +flying 2 +cloth 2 +gazed 2 +profit 2 +fourth 2 +bell 2 +hitherto 2 +benefit 2 +movements 2 +eagerly 2 +acted 2 +urged 2 +ascii 2 +disposed 2 +electronically 2 +atmosphere 2 +chapter 2 +begged 2 +hole 2 +invited 2 +borne 2 +departed 2 +catholic 2 +files 2 +reasonable 2 +sugar 2 +replacement 2 +sigh 2 +humanity 2 +thrust 2 +frame 2 +opposition 2 +disk 2 +haste 2 +lonely 2 +artist 2 +knight 2 +quarters 2 +charm 2 +substance 2 +rolled 2 +email 2 +flung 2 +celebrated 2 +division 2 +slavery 2 +verse 2 +decision 2 +probable 2 +painful 2 +governor 2 +forever 2 +turns 2 +branch 2 +ocean 2 +rear 2 +leader 2 +delightful 2 +stared 2 +boats 2 +keen 2 +disposition 2 +senses 2 +occasions 2 +readable 2 +beloved 2 +inches 2 +bones 2 +enthusiasm 2 +materials 2 +luck 2 +derived 2 +managed 2 +community 2 +apparent 2 +preserved 2 +magnificent 2 +hurry 2 +scheme 2 +oil 2 +thence 2 +reaching 2 +dim 2 +wretched 2 +hanging 2 +pipe 2 +useless 2 +nevertheless 2 +print 2 +smooth 2 +solid 2 +pursued 2 +necessarily 2 +build 2 +attempted 2 +centuries 2 +eggs 2 +equivalent 2 +hastily 2 +burned 2 +you’d 2 +recent 2 +oh 2 +travel 2 +cries 2 +noon 2 +crying 2 +generations 2 +located 2 +cabin 2 +announcement 2 +Britain 2 +compared 2 +handed 2 +cease 2 +smaller 2 +circumstance 2 +tent 2 +frequent 2 +alarm 2 +nervous 2 +beast 2 +what’s 2 +aloud 2 +independent 2 +gates 2 +distinction 2 +essential 2 +observation 2 +stronger 2 +recovered 2 +belonging 2 +loving 2 +masters 2 +writers 2 +cf. 2 +permanent 2 +mortal 2 +stern 2 +gratitude 2 +preserve 2 +burden 2 +aspect 2 +millions 2 +merry 2 +knife 2 +dread 2 +clever 2 +applicable 2 +district 2 +shadows 2 +silk 2 +failure 2 +links 2 +cent 2 +sentiment 2 +amid 2 +profits 2 +agent 2 +finds 2 +Russia 2 +bade 2 +Russian 2 +desperate 2 +union 2 +imagined 2 +contempt 2 +raising 2 +lords 2 +hell 2 +separated 2 +grant 2 +seriously 2 +tribes 2 +hit 2 +enormous 2 +defective 2 +conviction 2 +secured 2 +mixed 2 +insisted 2 +wooden 2 +prefer 2 +prayers 2 +fever 2 +selected 2 +daughters 2 +treat 2 +warning 2 +flew 2 +speaks 2 +developed 2 +impulse 2 +slipped 2 +ours 2 +mistaken 2 +damages 2 +ambition 2 +resumed 2 +yield 2 +ideal 2 +schools 2 +confirmed 2 +descended 2 +rush 2 +falls 2 +deny 2 +calculated 2 +correct 2 +perform 2 +hadn’t 2 +somehow 2 +accordingly 2 +stayed 2 +acquired 2 +counsel 2 +distress 2 +sins 2 +notion 2 +discussion 2 +constitution 2 +hundreds 2 +instrument 2 +firmly 2 +actions 2 +steady 2 +remarks 2 +empire 2 +elements 2 +idle 2 +pen 2 +entering 2 +online 2 +Africa 2 +permit 2 +th’ 2 +tide 2 +vol 2 +leaned 2 +college 2 +maintain 2 +sovereign 2 +tail 2 +generation 2 +crowded 2 +fears 2 +nights 2 +limitation 2 +tied 2 +horrible 2 +cat 2 +displaying 2 +port 2 +male 2 +experienced 2 +opposed 2 +treaty 2 +contents 2 +rested 2 +mode 2 +poured 2 +les 2 +occur 2 +seeking 2 +practically 2 +abandoned 2 +reports 2 +eleven 2 +sank 2 +begins 2 +founded 2 +brings 2 +trace 2 +instinct 2 +collected 2 +Scotland 2 +characteristic 2 +chose 2 +cheerful 2 +tribe 2 +costs 2 +threatened 2 +arrangement 2 +western 2 +sang 2 +beings 2 +pressure 2 +politics 2 +sorts 1 +shelter 1 +rude 1 +scientific 1 +revealed 1 +winds 1 +riding 1 +scenes 1 +shake 1 +industry 1 +claims 1 +pp. 1 +merit 1 +profession 1 +lamp 1 +interview 1 +territory 1 +sleeping 1 +sex 1 +coffee 1 +devotion 1 +thereof 1 +creation 1 +trail 1 +Romans 1 +supported 1 +requires 1 +fathers 1 +prospect 1 +obey 1 +shone 1 +operation 1 +northern 1 +nurse 1 +profound 1 +hungry 1 +sisters 1 +assure 1 +exceedingly 1 +match 1 +wrath 1 +continually 1 +rest. 1 +gifts 1 +folly 1 +chain 1 +uniform 1 +debt 1 +teaching 1 +venture 1 +execution 1 +shoes 1 +mood 1 +crew 1 +perceive 1 +accounts 1 +eating 1 +multitude 1 +declare 1 +yard 1 +astonishment 1 +version 1 +vague 1 +odd 1 +grateful 1 +nearest 1 +infinite 1 +elsewhere 1 +copying 1 +apartment 1 +activity 1 +wives 1 +parted 1 +security 1 +cared 1 +sensible 1 +owing 1 +Saturday 1 +cottage 1 +Jews 1 +leaning 1 +capacity 1 +joe 1 +settle 1 +referred 1 +holder 1 +involved 1 +sunshine 1 +Dutch 1 +council 1 +princes 1 +ate 1 +examination 1 +steel 1 +strangers 1 +beheld 1 +test 1 +noted 1 +slightest 1 +widow 1 +charity 1 +realized 1 +element 1 +shed 1 +errors 1 +communication 1 +reflection 1 +attacked 1 +organization 1 +maintained 1 +restored 1 +folks 1 +concealed 1 +accordance 1 +heavens 1 +star 1 +examined 1 +deeds 1 +wordforms 1 +somebody 1 +incident 1 +oath 1 +guest 1 +bar 1 +row 1 +poverty 1 +bottle 1 +prevented 1 +bless 1 +stir 1 +intense 1 +completed 1 +quarrel 1 +touching 1 +inner 1 +available 1 +fix 1 +resistance 1 +unusual 1 +deed 1 +derive 1 +hollow 1 +suspected 1 +contains 1 +sighed 1 +province 1 +deserted 1 +establishment 1 +vote 1 +muttered 1 +thither 1 +oxford 1 +cavalry 1 +lofty 1 +endure 1 +succeed 1 +leg 1 +bid 1 +hated 1 +civilization 1 +u.s. 1 +acting 1 +landed 1 +passions 1 +interior 1 +scarce 1 +lightly 1 +disturbed 1 +rev 1 +supreme 1 +hang 1 +notwithstanding 1 +shock 1 +exception 1 +offering 1 +display 1 +strain 1 +drank 1 +confined 1 +exhausted 1 +poets 1 +sounded 1 +aim 1 +critical 1 +directions 1 +negro 1 +fearful 1 +standard 1 +studied 1 +bag 1 +buildings 1 +consequences 1 +commenced 1 +deeper 1 +repeat 1 +driving 1 +beasts 1 +track 1 +rid 1 +holds 1 +residence 1 +steadily 1 +intimate 1 +drinking 1 +swear 1 +treasure 1 +fun 1 +throwing 1 +apt 1 +enterprise 1 +queer 1 +seed 1 +tower 1 +runs 1 +defend 1 +favourite 1 +desires 1 +heavily 1 +assembled 1 +existed 1 +depends 1 +poems 1 +hesitated 1 +stuff 1 +section 1 +settlement 1 +staring 1 +sole 1 +roads 1 +plate 1 +Mexico 1 +overcome 1 +pains 1 +performing 1 +dwell 1 +grounds 1 +taxes 1 +marble 1 +recently 1 +tones 1 +ability 1 +awake 1 +wave 1 +shaking 1 +folk 1 +possibility 1 +butter 1 +fury 1 +marched 1 +writes 1 +issued 1 +sailed 1 +instructions 1 +hatred 1 +pursuit 1 +pull 1 +furniture 1 +additions 1 +hid 1 +rope 1 +vi 1 +adventure 1 +royalty 1 +vanished 1 +arts 1 +elder 1 +signal 1 +wanting 1 +supplied 1 +feast 1 +safely 1 +burn 1 +describe 1 +references 1 +lesson 1 +annual 1 +card 1 +passes 1 +application 1 +intelligent 1 +county 1 +beaten 1 +presents 1 +format 1 +flow 1 +sixty 1 +scale 1 +damage 1 +marks 1 +obtaining 1 +moreover 1 +commerce 1 +startled 1 +southern 1 +consequently 1 +outer 1 +belongs 1 +ben 1 +wrought 1 +average 1 +naked 1 +conducted 1 +rivers 1 +songs 1 +obvious 1 +foundation 1 +concern 1 +ceremony 1 +magic 1 +campaign 1 +hunting 1 +liberal 1 +whisper 1 +largely 1 +commonly 1 +torn 1 +exists 1 +contributions 1 +hunt 1 +teacher 1 +lawyer 1 +operations 1 +detail 1 +shortly 1 +wondering 1 +leaders 1 +blessing 1 +princess 1 +he’d 1 +altar 1 +tenderness 1 +tiny 1 +web 1 +cardinal 1 +sharply 1 +regiment 1 +chest 1 +distinctly 1 +purple 1 +creating 1 +gather 1 +depth 1 +indignation 1 +performance 1 +election 1 +prosperity 1 +gloomy 1 +conception 1 +clerk 1 +decide 1 +drunk 1 +victim 1 +reflected 1 +pour 1 +preceding 1 +individuals 1 +gazing 1 +absurd 1 +lift 1 +gesture 1 +armies 1 +limbs 1 +manage 1 +brethren 1 +plays 1 +hastened 1 +dragged 1 +motive 1 +whatsoever 1 +pointing 1 +verses 1 +pronounced 1 +exchange 1 +definite 1 +emperor 1 +tendency 1 +remote 1 +finish 1 +flag 1 +boots 1 +enabled 1 +administration 1 +denied 1 +churches 1 +rarely 1 +earnestly 1 +considering 1 +previously 1 +ugly 1 +bears 1 +signed 1 +genuine 1 +harmless 1 +mingled 1 +obedience 1 +walks 1 +training 1 +badly 1 +feed 1 +central 1 +contrast 1 +relieved 1 +romance 1 +Mississippi 1 +structure 1 +payment 1 +pace 1 +passages 1 +succession 1 +persuaded 1 +sources 1 +inquiry 1 +inspired 1 +angels 1 +roll 1 +wilt 1 +inch 1 +troubles 1 +perfection 1 +wherever 1 +owe 1 +handle 1 +advantages 1 +trip 1 +shoot 1 +fortunate 1 +newspaper 1 +employment 1 +fitted 1 +refuge 1 +misfortune 1 +providence 1 +owns 1 +cutting 1 +beard 1 +stirred 1 +tear 1 +resist 1 +depths 1 +maiden 1 +determine 1 +commission 1 +merchant 1 +whereas 1 +crossing 1 +independence 1 +lively 1 +breeze 1 +provinces 1 +virtues 1 +conceived 1 +relative 1 +solitary 1 +smell 1 +wandering 1 +thereby 1 +eighteen 1 +locked 1 +provision 1 +courts 1 +eaten 1 +historical 1 +regarding 1 +preferred 1 +pick 1 +ruined 1 +wherein 1 +vanity 1 +condemned 1 +deliver 1 +unexpected 1 +desk 1 +gross 1 +lane 1 +happens 1 +represent 1 +root 1 +Holland 1 +mud 1 +respectable 1 +cleared 1 +feels 1 +fruits 1 \ No newline at end of file diff --git a/lang/fr/wordlist.txt b/lang/fr/wordlist.txt index 11da389..58f8e33 100644 --- a/lang/fr/wordlist.txt +++ b/lang/fr/wordlist.txt @@ -1,1972 +1,3333 @@ -de -je -est -pas -le -vous -la -tu -que -un -il -et -à -a -ne -les -ce -en -on -ça -une -ai -pour -des -moi -qui -nous -mais -y -me -dans -du -bien -elle -si -tout -plus -non -mon -suis -te -au -avec -oui -va -toi -fait -ils -as -être -faire -se -comme -était -sur -quoi -ici -sais -lui -veux -ma -là -rien -dit -es -où -votre -pourquoi -sont -cette -quand -par -son -ton -peux -alors -dire -vais -comment -avez -bon -ou -très -même -merci -ont -jamais -aussi -chose -voir -allez -tous -ces -deux -sa -faut -été -êtes -ta -avoir -fais -peut -autre -maintenant -encore -peu -vraiment -m -mes -temps -toujours -notre -vie -oh -juste -sans -avait -quelque -monde -accord -vu -fois -aller -trop -viens -crois -dois -père -dieu -homme -sûr -aux -leur -avant -étais -besoin -femme -personne -avais -aime -chez -vrai -ans -ses -mal -parler -vos -après -mort -ca -eu -veut -parce que -sera -mieux -bonne -petit -tes -dis -beaucoup -monsieur -voilà -depuis -doit -mère -quel -vas -vois -fille -déjà -gens -donc -jour -il -autres -soir -toute -ouais -argent -maison -nom -bonjour -pense -nos -cela -nuit -avons -ii -merde -cet -papa -maman -reste -peur -désolé -salut -seul -arrive -vite -prendre -regarde -soit -air -quelle -passé -trois -savoir -plaît -choses -fils -ah -bas -moins -entre -passe -hé -demain -appelle -grand -tête -voulez -faites -arrête -hein -attends -ok -raison -enfants -assez -aurais -elles -voulais -sommes -jours -parle -moment -amour -toutes -heure -puis -tard -tuer -eh -dû -ami -petite -partir -hommes -connais -aider -savez -gars -chance -combien -tant -sait -part -voiture -pris -problème -aujourd’hui -coup -porte -serait -prends -venir -travail -pu -famille -seule -sens -allons -putain -idée -ni -contre -revoir -entendu -comprends -passer -pendant -trouvé -trouver -quelques -vient -vieux -aurait -attention -demande -chercher -sous -pouvez -voici -pourrait -sang -histoire -amis -sortir -question -venez -rester -frère -ville -fini -nouveau -eux -truc -tiens -yeux -mois -laisse -mec -longtemps -belle -police -seulement -importe -heures -eau -car -super -chaque -cas -vont -tué -terre -place -main -ensemble -type -beau -pardon -vers -aucun -guerre -trouve -partie -suite -prie -devant -compris -arrivé -mme -leurs -étaient -mettre -matin -aide -dessus -sois -genre -fin -perdu -jeune -chérie -premier -attendez -enfant -donne -venu -aimerais -droit -côté -chambre -loin -donner -devrais -laisser -feu -jouer -ie -train -gros -compte -s -savais -mourir -pouvoir -regardez -parlé -donné -première -aura -dernière -minutes -aucune -mari -enfin -madame -façon -devrait -mis -film -femmes -fort -pourrais -écoute -pays -parti -affaire -endroit -corps -ia -fou -vivre -prêt -dont -espère -grande -cause -point -filles -dehors -hier -boulot -pensais -garçon -près -désolée -cinq -chef -ainsi -haut -celui -demandé -dirait -bébé -possible -école -plein -mains -années -dites -nouvelle -manger -docteur -tour -croire -ceux -quatre -plutôt -marche -semaine -vérité -envie -capitaine -arrêter -affaires -bientôt -vue -demander -dernier -instant -essaie -arriver -font -tellement -derrière -tomber -presque -voulait -meilleur -numéro -journée -appeler -dollars -attendre -confiance -garde -souviens -dur -serai -bureau -voyez -abord -important -devez -ben -peine -cours -fera -prend -seigneur -suffit -route -cul -ils -minute -bonsoir -jeu -croyais -ferme -plaisir -voudrais -heureux -mot -musique -chien -messieurs -prenez -calme -parents -dedans -fous -arrêtez -mariage -entrer -rentrer -ait -lit -voit -autant -reviens -parfait -coeur -ceci -service -téléphone -pauvre -mlle -attend -drôle -ira -parfois -retour -á -verre -pensé -six -ci -impossible -ferai -aimes -payer -facile -maître -new -appelé -mauvais -général -doute -prison -adore -faute -entends -oublié -bras -exactement -fête -café -chéri -sors -gentil -penser -vaut -john -lieu -malade -changer -roi -commence -entendre -président -travailler -partout -cher -morts -rendre -écrit -équipe -joue -sinon -esprit -regarder -rentre -plan -cœur -veulent -montrer -voulu -boire -travaille -propre -année -état -soyez -bois -cherche -laissé -essayer -dès -semble -ies -jack -dix -faisait -génial -penses -sécurité -tôt -mets -rêve -armée -perdre -parles -avis -surtout -difficile -york -dormir -ensuite -pire -ecoute -simple -allait -paix -morte -arme -sujet -retard -voyons -livre -appris -peuvent -sale -serais -souvent -an -sauf -choix -sûrement -étiez -or -visage -ordre -comprendre -essayé -noir -dîner -âge -chemin -changé -bout -face -rue -inquiète -photo -allé -personnes -sérieux -ciel -honneur -amie -questions -force -garder -petits -tirer -millions -grave -marché -nouvelles -voix -semaines -pouvais -courant -propos -bateau -oublie -con -celle -gauche -content -prix -rouge -faim -ferais -avion -devenir -devoir -prochaine -restez -acheter -voyage -sorte -long -espèce -idiot -gueule -début -bouge -continue -hôpital -sort -grâce -problèmes -message -certains -patron boss -sûre -reçu -trucs -avaient -ouvre -promis -the -oncle -euh -connaissez -laissez -devons -bienvenue -occupe -allais -camp -manque -soleil -devait -pars -pouvait -cheveux -armes -pensez -salle -croyez -bizarre -gagner -commencé -fond -sauver -pièce -erreur -ailleurs -rapport -froid -apprendre -scène -secret -sac -seconde -cru -allô -revenir -battre -hôtel -soirée -aurai -soeur -pieds -seras -carte -jolie -n -pied -groupe -venue -monte -agent -fallait -effet -libre -seront -foutre -bordel -mots -neuf -tire -faux -situation -tue -lumière -vieille -droite -debout -noël -cheval -intérieur -écoutez -joli -gagné -loi -entrez -auras -incroyable -lettre -présent -occuper -connaît -absolument -aviez -dame -professeur -fric -george -retrouver -coin -table -colonel -âme -dos -magnifique -rencontrer -réussi -meilleure -rappelle -tranquille -fut -chaud -commencer -sœur -agit -cool -doucement -pareil -jeunes -joe -accident -appel -anniversaire -blanc -white -finir -risqué -doivent -moyen -terminé -complètement -clair -meurtre -sam -parlez -heureuse -ordres -disais -touche -frank -prêts -fasse -puisse -déjeuner -envoyé -lire -disait -tombe -su -marcher -avance -déteste -forme -â -mauvaise -bord -décidé -mer -vivant -médecin -midi -devenu -montre -porter -vraie -disent -ferait -ignore -charlie -connaître -silence -diable -sortez -tom -cadeau -suppose -flics -avocat -jure -anglais -sept -devriez -moitié -aimé -surprise -chacun -serez -exact -commandant -télé -autour -disparu -ligne -expliquer -aimer -arrivée -grosse -simplement -mission -monter -tenir -bons -balle -quitter -selon -classe -paris -sorti -mange -peuple -habitude -voie -dangereux -pote -contrôle -prête -honte -rencontré -photos -impression -folle -écrire -suivre -oublier -livres -aille -retourner -offre -chanson -envoie -trou -arrière -poste -amérique -huit -radio -paul -ressemble -attaque -arrêté -change -baiser -pourtant -réponse -connu -connard -lu -pute -bande -enfer -tenez -rend -triste -tel -bravo -être -savait -david -plusieurs -existe -met -rire -compagnie -étrange -exemple -devais -vit -combat -secours -aurez -conneries -visite -joué -michael -pourra -coucher -imagine -bob -merveilleux -continuer -écoutez -dirai -volé -lune -bouche -sud -danse -ennuis -but -aie -hors -sortie -boîte -vol -public -lieutenant -écouter -présente -système -époque -retourne -certain -bête -vendre -avenir -grands -coups -santé -partez -amoureux -envoyer -cuisine -normal -danger -gouvernement -village -poser -ouvrir -journal -approche -faudra -dommage -peau -pleine -voler -nez -danser -servir -sympa -bonnes -mille -héros -banque -sergent -clé -secondes -nord -inspecteur -hey -liberté -salaud -plait -cour -juge -viennent -bruit -flic -tient -terrible -paraît -crime -prochain -pouvons -assis -thé -bonheur -payé -tas -travers -stupide -blague -préfère -conseil -robe -tiré -protéger -rêves -seuls -pitié -oeil -mêmes -vin -avions -don -sol -vent -club -garçons -gamin -tante -croit -mesdames -bar -milieu -reine -signe -centre -probablement -bière -dingue -inutile -nulle -mike -sent -différent -emmener -comprenez -verra -prise -vêtements -liste -savent -unis -société -chère -soin -pierre -appelez -utiliser -acheté -laquelle -fiche -parole -mecs -faisais -marier -départ -ennemi -irai -aimez -spectacle -vouloir -recherche -devient -choisi -you -pose -films -intérêt -intéresse -rôle -johnny -félicitations -descendre -harry -tourne -position -sert -blessé -humain -asseoir -match -coupable -environ -art -mise -londres -espoir -quitté -lequel -mur -peter -ouvert -église -salope -beauté -lâche -adresse -mademoiselle -petites -sauter -colère -directeur -adieu -jimmy -faisons -parie -entrée -tort wrong -conduire -américain -revenu -justice -soldat -expérience -auriez -cerveau -fenêtre -quartier -prince -vis -promets -venus -soldats -tombé -riche -fleurs -bill -mary -œil -présenter -presse -frères -ridicule -preuve -ouvrez -revient -pourriez -épouser -intéressant -gosse -faudrait -histoires -rose -veuillez -nature -vide -responsable -courage -capable -cinéma -max -décision -taxi -chanter -excuse -sentir -jim -portes -james -fier -deuxième -eiie -appartement -contact -essayez -sauvé -cacher -ià -répondre -manière -aimais -jambes -occasion -défense -longue -jésus -jeter -allée -formidable -base -telle -san -glace -dents -parmi -immédiatement -marié -paie -nul -machine -vacances -épouse -monstre -souvenir -tueur -né -continuez -français -rendu -course -majesté -mien -types -certainement -coupe -importance -ouest -idées -gagne -oubliez -chat -ayez -trouves -dégage -chier -ravi -étions -arrivent -joie -prévu -lorsque -superbe -touché -bain -contente -horrible -retrouve -court -dérange -and -bombe -ramener -certaines -réalité -enquête -camion -foutu -signifie -cent -toucher -occupé -attendais -rencontre -excellent -respect -quitte -tourner -terrain -blanche -projet -éviter -poisson -plupart -descends -joyeux -empêcher -mariée -mangé -envers -km -ange -meilleurs -copine -chaussures -dossier -règles -dure -animaux -langue -américains -imbécile -chevaux -princesse -zone -pius -drogue -charge -enfoiré -nourriture -pont -refuse -chiens -contraire -enchanté -douleur -politique -faits -arrivera -essaye -magasin -soient -dirais -entier -vécu -bus -offrir -chapeau -raconte -cartes -henry -discuter -papier -action -permis -bu -rapide -shérif -frais -partis -emmène takes -réunion -mettez -île -toilettes -code -billy -opération -spécial -planète -viendra -champ -couleur -pain -excuser -destin -rends -découvert -puisque -taille -nick -tony -vaisseau -sexe -sacré -repas -contrat -qu -nécessaire -client -détruire -lait -faite -mémoire -pleurer -copain -miss -restes -ray -personnel -double -mignon -couteau -sache -témoin -foi -dors -remercie -direction -steve -malin -niveau -remettre -habite -apporté -aiment -mienne -procès -doux -eddie -trouvez -solution -raconter -fermer -choisir -goût -nouveaux -end -amuser -différence -stop -charles -gosses -réfléchir -clients -enceinte -beaux -université -bleu -mètres -vus -aimait -marre -menti -appelles -courir -arranger -travaillé -papiers -retrouvé -officier -espace -sourire -dise -mérite -lettres -accepter -quels -failli -angleterre -vitesse -jambe -caméra -rappeler -arbre -pièces -forces -trésor -victime -énergie -disons -énorme -rentré -alex -appartient -préparer -propres -regrette -finalement -france -invité -moindre -censé -washington -bobby -image -justement -compter -enlever -chasse -unique -fout -arrêt -file -balles -preuves -dimanche -lycée -fil -morceau -noms -durant -sarah -cassé -doigts -vérifier -million -appareil -tirez -apporte -réponds -piste -derniers -troisième -dépend -humains -programme -honnête -voitures -bougez -richard -belles -régler -flingue -couper -noire -entendez -aveugle -bouger -venait -présence -savons -crise -amène -interdit -trés -lever -partons -clés -obtenir -pluie -récupérer -grandes -quelles -prouver -souris -restaurant -regard -okay -intention -forte -danny -cou -points -manqué -proche -urgence -folie -ancien -relation -bouteille -étage -rejoindre -casse -jardin -malgré -oiseau -méchant -raisons -perds -pleure -meurt -claire -frappé -chante -pourrai -nombre -robert -gentille -pourras -sentiments -regardé -toit -lors -métier -maladie -poche -três -frapper -tommy -succès -étant -lee -dessous -voudrait -théâtre -chinois -crains -afin -gare -billets -douce -frappe -remarqué -paradis -étranger -campagne -fermé -alcool -jerry -vendu -samedi -parlait -produit -répète -amoureuse -souhaite -odeur -appelait -vôtre -attendant -montagne -oû -vies -demandez -fantastique -victoire -mens -carrière -enlève -fatigué -suivi -animal -raté -règle -meurs -remercier -assurer -queue -viande -rivière -falloir -suivant -assure -obligé -martin -fusil -passage -hasard -pauvres -neige -parfaitement -journaux -échapper -plage -secrets -signal -crétin -aies -vidéo -coffre -pression -costume -informations -futur -disant -univers -auraient -gardes -lis -volonté -attendu -verras -donnez -demandais -excuses -vienne -pensent -faible -cache -fiston -van -prendra -attraper -dort -décider -inquiétez -cesse -gaffe -serons -ministre -naissance -agir -utile -gaz -bataille -noirs -vouliez -retraite -voleur -poids -discours -nouvel -gâteau -cent -ventre -connaissance -voulons -vive -blancs -assassin -jeux -vendredi -europe -accepte -parlons -certaine -couple -sérieusement -militaire -date -rome -entend -droits -resté -montez -titre -génie -couilles -revenez -recevoir -autrement -répondu -amené -os -perd -valeur -puissant -style -vert -fortune -vieil -major -amener -prépare -poulet -embrasser -genoux -acte -détruit -célèbre -cible -cousin -conscience -prévenir -ramène -arrivés -doigt -californie -article -cherchez -étoiles -milliers -reprendre -mine -regardes -servi -ennemis -charmant -sentiment -reposer -note -couché -gardez -jake -agréable -anna -préféré -château -bal -oreilles -découvrir -apparemment -l’os -zéro -saint -côtés -marrant -comprend -pourrez -joues -salon -lève -utilise -conversation -pilote -fer -attendent -humaine -conduit -gorge -victimes -détails -talent -passé -iui -Marie -privé -apporter -tuée -Le Havre -violence -feras -nerveux -aéroport -direct -enculé -tribunal -commande -jette -paquet -fumer -aise -dieux -emmerde -joindre -lance -usine -défendre -forêt -champion -horreur -importante -paroles -test -désormais -tres -dernières -tombée -Jaques -extérieur -désert -Louis -Angelique -coupé -intelligent -Calais -aide -saura -dira -job -passera -comptes -mode -parlais -invités -série -pis -sage -rappelles -concerne -mariés -grandi -pousse -vache -accepté -meme -pensait -repos -ordinateur -rock -allo -Paris -ombre -russe -minuit -vole -amusant -membres -jane -moyens -surpris -ouverte -nombreux -également -imaginer -mince -dormi -page -essence -maire -faisant -lundi -entré -billet -moteur -réparer -franchement -caché -cigarette -riches -partager -puissance -trouvée -parfaite -hiver -épée -reviendra -haute -souffle -tente -vingt -miracle -arbres -apprécie -senti -manquer -artiste -créer -lac -jeté -souvenirs -sommeil -vrais -réveiller -Lyon -leçon -casser -courses -chemise -fantôme -Fribourg -membre -passez -acteur -battu -parc -Bruxelles -Montreal -services -rues -reculez -japonais -Lourdes -mentir -Marseille -rentrez -plans -Arles -coûte -Champagne -américaine -risques -pète -offert -Orléans -semblant -annonce -Rouen -échange -Lille -premiers -débarrasser -fuir -Supplie -saute -signer \ No newline at end of file +de 1725 +la 1030 +et 814 +le 769 +à 722 +les 630 +en 488 +des 481 +un 465 +il 459 +que 386 +une 381 +est 373 +du 337 +dans 317 +qui 286 +pour 262 +pas 258 +je 242 +elle 220 +ne 219 +au 212 +ce 210 +se 204 +par 195 +a 193 +sur 187 +plus 171 +son 168 +mais 137 +avec 133 +vous 129 +on 125 +était 120 +lui 120 +nous 118 +ou 117 +sa 117 +comme 109 +avait 105 +ses 100 +tout 96 +cette 94 +si 88 +sont 86 +même 85 +être 83 +me 81 +y 80 +bien 79 +ils 78 +aux 78 +d’un 69 +tu 68 +leur 66 +fait 65 +d’une 63 +ces 61 +sans 61 +ai 59 +deux 58 +peut 55 +faire 54 +où 53 +aussi 53 +mon 52 +ont 47 +été 47 +entre 45 +dit 45 +moi 44 +autre 43 +non 41 +tous 41 +temps 40 +encore 39 +très 39 +après 38 +alors 38 +autres 38 +là 37 +ma 36 +dont 36 +quand 36 +peu 36 +sous 34 +ça 33 +vie 33 +avoir 32 +leurs 32 +ainsi 32 +fois 32 +avant 31 +cela 31 +rien 30 +vers 30 +homme 29 +toujours 29 +puis 29 +suis 29 +dire 28 +donc 28 +toute 28 +moins 27 +monde 26 +jamais 26 +quelques 26 +notre 26 +voir 25 +étaient 25 +elles 24 +depuis 24 +mes 24 +votre 24 +contre 23 +soit 23 +grand 23 +jusqu’ 22 +yeux 22 +celui 22 +toutes 22 +cet 22 +car 22 +chez 21 +tête 21 +cas 21 +te 21 +jour 20 +trois 20 +ans 20 +faut 20 +ici 20 +chose 20 +point 20 +trop 19 +celle 19 +déjà 19 +fut 19 +femme 19 +petit 19 +ni 18 +va 18 +père 18 +moment 18 +droit 18 +main 18 +avaient 18 +devant 18 +place 18 +état 17 +pays 17 +quelque 17 +comment 17 +travail 17 +personne 17 +nos 17 +chaque 17 +ceux 16 +pendant 16 +doit 16 +eux 16 +partie 16 +grande 16 +corps 16 +bon 16 +mère 16 +sens 16 +part 16 +porte 16 +aurait 16 +histoire 16 +premier 16 +parce 16 +avais 16 +France 16 +hommes 16 +première 16 +pourquoi 15 +beaucoup 15 +dieu 15 +effet 15 +toi 15 +tant 15 +souvent 15 +années 15 +ci 15 +l’on 14 +nouveau 14 +jours 14 +jeune 14 +oui 14 +quoi 14 +l’autre 14 +pouvoir 14 +question 14 +plusieurs 14 +ton 14 +prendre 14 +mort 14 +côté 14 +mal 14 +fin 14 +d’autres 14 +enfants 14 +maison 14 +serait 14 +seul 13 +nom 13 +lieu 13 +politique 13 +voix 13 +compte 13 +également 13 +selon 13 +enfin 13 +ville 13 +seulement 13 +cœur 13 +mieux 13 +air 13 +eu 13 +sais 13 +exemple 13 +avons 13 +regard 13 +enfant 12 +d’être 12 +petite 12 +reste 12 +savoir 12 +sera 12 +raison 12 +fit 12 +lorsque 12 +maintenant 12 +avez 12 +coup 12 +dès 12 +eau 12 +ailleurs 12 +façon 12 +près 12 +terre 12 +esprit 12 +nouvelle 12 +pouvait 11 +nuit 11 +besoin 11 +ensemble 11 +l’homme 11 +partir 11 +art 11 +certains 11 +forme 11 +seule 11 +pu 11 +famille 11 +autant 11 +fille 11 +saint 11 +hui 11 +bras 11 +heures 11 +loin 11 +aujourd’ 11 +cours 11 +étais 11 +surtout 11 +vu 11 +guerre 11 +société 11 +bonne 11 +face 11 +peuvent 11 +idée 11 +manière 11 +vue 11 +choses 11 +quatre 11 +aller 11 +doute 10 +assez 10 +mois 10 +personnes 10 +amour 10 +siècle 10 +français 10 +femmes 10 +mettre 10 +nombre 10 +juste 10 +parler 10 +fils 10 +possible 10 +autour 10 +tard 10 +gens 10 +lors 10 +aucun 10 +passé 10 +mains 10 +veux 10 +vraiment 10 +nature 10 +heure 10 +demande 10 +loi 10 +laquelle 10 +pris 10 +rapport 10 +pourtant 10 +général 9 +chambre 9 +suite 9 +visage 9 +lorsqu’ 9 +presque 9 +aucune 9 +es 9 +situation 9 +vos 9 +soir 9 +cependant 9 +parfois 9 +l’un 9 +afin 9 +trouve 9 +fort 9 +ordre 9 +dernier 9 +plutôt 9 +long 9 +faisait 9 +passer 9 +quel 9 +donner 9 +peine 9 +cause 9 +œuvre 9 +roi 9 +or 9 +force 9 +dessus 9 +sujet 8 +propre 8 +agit 8 +tour 8 +pierre 8 +derrière 8 +bas 8 +mise 8 +vrai 8 +sûr 8 +abord 8 +mots 8 +sommes 8 +rue 8 +mot 8 +année 8 +haut 8 +article 8 +système 8 +pourrait 8 +trouver 8 +semble 8 +ensuite 8 +ayant 8 +action 8 +service 8 +longtemps 8 +cinq 8 +groupe 8 +livre 8 +mis 8 +prix 8 +grands 8 +plan 8 +tel 8 +milieu 8 +mesure 8 +mêmes 8 +permet 8 +objet 8 +étant 7 +êtes 7 +notamment 7 +travers 7 +telle 7 +instant 7 +niveau 7 +devait 7 +monsieur 7 +ta 7 +début 7 +développement 7 +sorte 7 +grâce 7 +quelle 7 +l’État 7 +veut 7 +dix 7 +retour 7 +font 7 +vais 7 +donne 7 +simple 7 +fond 7 +demanda 7 +présent 7 +peux 7 +d’abord 7 +l’air 7 +sourire 7 +bout 7 +chacun 7 +malgré 7 +tandis 7 +rendre 7 +époque 7 +peur 7 +parents 7 +table 7 +public 7 +passe 7 +belle 7 +chapitre 7 +recherche 7 +entreprise 7 +parmi 7 +vite 7 +rôle 7 +matin 7 +dis 7 +vient 7 +dernière 7 +allait 7 +centre 7 +l’histoire 7 +donné 7 +comprendre 7 +espace 7 +voilà 7 +présence 7 +répondit 7 +conseil 7 +réalité 7 +choix 7 +valeur 7 +jeunes 7 +moyen 7 +type 7 +sait 7 +lequel 7 +ait 7 +fonction 7 +eut 7 +crois 7 +celles 7 +chef 7 +laisser 7 +intérieur 6 +lit 6 +mouvement 6 +certaines 6 +âge 6 +dû 6 +d’ailleurs 6 +sang 6 +d’avoir 6 +route 6 +lumière 6 +petits 6 +états 6 +projet 6 +figure 6 +cour 6 +attention 6 +salle 6 +beau 6 +aide 6 +nouvelles 6 +image 6 +envie 6 +quelqu’ 6 +argent 6 +l’avait 6 +texte 6 +bois 6 +chemin 6 +problème 6 +titre 6 +vivre 6 +cadre 6 +langue 6 +intérêt 6 +droits 6 +conditions 6 +silence 6 +école 6 +entrée 6 +feu 6 +terme 6 +voulait 6 +questions 6 +conscience 6 +direction 6 +vieux 6 +marché 6 +œil 6 +sociale 6 +prise 6 +pensée 6 +frère 6 +liberté 6 +nord 6 +genre 6 +lettre 6 +l’eau 6 +vingt 6 +française 6 +venir 6 +pièce 6 +vérité 6 +accord 6 +ami 6 +membres 6 +ciel 6 +ouvert 6 +maître 6 +nombreux 6 +compris 6 +plaisir 6 +politiques 6 +origine 6 +pense 6 +soleil 6 +train 6 +pied 6 +soi 6 +aime 6 +écrit 6 +amis 6 +minutes 6 +expérience 6 +sud 6 +église 6 +principe 6 +existence 6 +activité 6 +quant 5 +existe 5 +sol 5 +particulier 5 +auteur 5 +bouche 5 +président 5 +voiture 5 +l’a 5 +gouvernement 5 +penser 5 +réponse 5 +propos 5 +matière 5 +pratique 5 +certain 5 +âme 5 +base 5 +risque 5 +affaires 5 +affaire 5 +savait 5 +l’esprit 5 +grandes 5 +gauche 5 +fais 5 +qualité 5 +prend 5 +cheveux 5 +processus 5 +seconde 5 +d’autre 5 +simplement 5 +parle 5 +noir 5 +contraire 5 +venait 5 +relation 5 +scène 5 +social 5 +peuple 5 +mars 5 +garde 5 +expression 5 +juin 5 +économique 5 +différents 5 +sein 5 +semblait 5 +produits 5 +parti 5 +pieds 5 +mai 5 +mer 5 +doivent 5 +difficile 5 +santé 5 +services 5 +nécessaire 5 +l’idée 5 +plein 5 +arrive 5 +fallait 5 +organisation 5 +bureau 5 +éléments 5 +Europe 5 +culture 5 +laisse 5 +puisque 5 +prit 5 +droite 5 +entendu 5 +demander 5 +tes 5 +analyse 5 +sécurité 5 +journée 5 +plupart 5 +faisant 5 +différentes 5 +caractère 5 +voit 5 +aurais 5 +important 5 +six 5 +désormais 5 +discours 5 +formation 5 +dos 5 +sortir 5 +passage 5 +rouge 5 +relations 5 +entendre 5 +période 5 +parole 5 +chercher 5 +vois 5 +présente 5 +produit 5 +croire 5 +note 5 +l’une 5 +contrôle 5 +rester 5 +impression 5 +endroit 5 +but 5 +décision 5 +ligne 5 +jeu 5 +connaissance 5 +départ 5 +rire 5 +an 5 +ah 5 +durant 5 +deuxième 5 +signe 5 +position 5 +auprès 5 +lèvres 5 +hors 5 +blanc 5 +production 5 +oh 5 +madame 5 +l’enfant 5 +ancien 5 +toutefois 5 +aura 5 +vit 5 +mille 5 +données 5 +volonté 5 +occasion 4 +midi 4 +véritable 4 +devient 4 +faites 4 +libre 4 +confiance 4 +justice 4 +région 4 +appelle 4 +domaine 4 +mémoire 4 +bord 4 +d’elle 4 +désir 4 +premiers 4 +étude 4 +études 4 +gros 4 +cuisine 4 +acte 4 +moyens 4 +juillet 4 +l’article 4 +devenir 4 +armée 4 +l’amour 4 +longue 4 +voyage 4 +village 4 +modèle 4 +certaine 4 +particulièrement 4 +voie 4 +activités 4 +soient 4 +furent 4 +répondre 4 +rapidement 4 +musique 4 +sœur 4 +vis 4 +arrière 4 +cit 4 +importance 4 +charge 4 +disait 4 +met 4 +mari 4 +sentiment 4 +importe 4 +générale 4 +l’intérieur 4 +puisse 4 +vont 4 +tellement 4 +création 4 +entreprises 4 +montre 4 +formes 4 +peau 4 +termes 4 +code 4 +perdu 4 +idées 4 +davantage 4 +dois 4 +police 4 +mode 4 +nouveaux 4 +nationale 4 +information 4 +soudain 4 +dehors 4 +demi 4 +outre 4 +aussitôt 4 +propres 4 +l’ordre 4 +champ 4 +travaux 4 +impossible 4 +population 4 +hôtel 4 +bientôt 4 +filles 4 +double 4 +mit 4 +nombreuses 4 +livres 4 +carte 4 +mariage 4 +connaître 4 +énergie 4 +absence 4 +informations 4 +troisième 4 +arrivée 4 +page 4 +république 4 +rencontre 4 +d’en 4 +appel 4 +semaine 4 +gestion 4 +trouvé 4 +l’ensemble 4 +tableau 4 +visite 4 +retrouver 4 +environ 4 +voulu 4 +attendre 4 +lettres 4 +juge 4 +courant 4 +publique 4 +l’ai 4 +décembre 4 +janvier 4 +règles 4 +moindre 4 +delà 4 +paix 4 +régime 4 +points 4 +problèmes 4 +accès 4 +l’Église 4 +tenir 4 +tôt 4 +unique 4 +petites 4 +doigts 4 +aider 4 +bonheur 4 +seront 4 +éviter 4 +ouvrage 4 +classe 4 +suivant 4 +septembre 4 +anglais 4 +source 4 +ministre 4 +lieux 4 +approche 4 +l’heure 4 +forces 4 +large 4 +l’objet 4 +foi 4 +manque 4 +eh 4 +l’école 4 +avril 4 +octobre 4 +bruit 4 +forte 4 +café 4 +faits 4 +haute 4 +Afrique 4 +parties 4 +pouvez 4 +offre 4 +éducation 4 +demandé 4 +merci 4 +puissance 4 +août 4 +huit 4 +autorité 4 +évolution 4 +date 4 +combien 4 +avis 4 +regarde 4 +journal 4 +allez 4 +second 4 +mission 4 +sauf 4 +entrer 4 +union 4 +emploi 4 +suivre 4 +jouer 4 +cent 4 +humain 4 +chance 4 +usage 4 +contrat 4 +verre 4 +taille 4 +site 4 +sept 4 +connu 4 +voici 4 +allons 4 +trouvait 4 +commun 4 +l’époque 4 +national 4 +construction 4 +derniers 4 +groupes 4 +application 4 +communication 4 +humaine 4 +cité 4 +personnel 3 +unis 3 +finalement 3 +venu 3 +marche 3 +pratiques 3 +regarder 3 +physique 3 +d’eau 3 +protection 3 +partout 3 +douleur 3 +l’espace 3 +garçon 3 +avenir 3 +lire 3 +novembre 3 +effets 3 +pleine 3 +communauté 3 +colère 3 +maman 3 +l’année 3 +économie 3 +voire 3 +propose 3 +certes 3 +vent 3 +épaules 3 +sort 3 +no 3 +pauvre 3 +montrer 3 +exercice 3 +résultats 3 +proche 3 +traitement 3 +île 3 +moitié 3 +vide 3 +joie 3 +perdre 3 +actions 3 +taux 3 +lien 3 +eût 3 +premières 3 +université 3 +téléphone 3 +heureux 3 +crise 3 +succès 3 +geste 3 +valeurs 3 +lendemain 3 +critique 3 +l’impression 3 +environnement 3 +l’art 3 +devoir 3 +objets 3 +tomber 3 +science 3 +adresse 3 +actes 3 +équipe 3 +paroles 3 +égard 3 +assurer 3 +devenu 3 +vol 3 +naissance 3 +commerce 3 +obtenir 3 +lecture 3 +leva 3 +froid 3 +vision 3 +contexte 3 +directement 3 +d’après 3 +vieille 3 +preuve 3 +tient 3 +durée 3 +changement 3 +résultat 3 +administration 3 +calme 3 +coups 3 +commence 3 +raisons 3 +front 3 +arriver 3 +couleur 3 +ressources 3 +seigneur 3 +sociales 3 +d’où 3 +joue 3 +mur 3 +identité 3 +mauvais 3 +condition 3 +capable 3 +théorie 3 +porter 3 +permis 3 +frais 3 +sociétés 3 +jardin 3 +coin 3 +terrain 3 +différence 3 +souvenir 3 +manger 3 +programme 3 +compagnie 3 +maladie 3 +ouest 3 +tenait 3 +arrivé 3 +liste 3 +larmes 3 +l’action 3 +lois 3 +demain 3 +objectif 3 +l’entreprise 3 +responsabilité 3 +rêve 3 +enseignement 3 +fini 3 +contact 3 +constitue 3 +travailler 3 +parfaitement 3 +facile 3 +possibilité 3 +semaines 3 +devrait 3 +février 3 +roman 3 +technique 3 +court 3 +vin 3 +respect 3 +honneur 3 +quartier 3 +révolution 3 +laissé 3 +paraît 3 +fenêtre 3 +langage 3 +territoire 3 +l’image 3 +millions 3 +goût 3 +expliquer 3 +faible 3 +médecin 3 +pose 3 +seuls 3 +regarda 3 +voulez 3 +prince 3 +cher 3 +découvrir 3 +théâtre 3 +textes 3 +changer 3 +religion 3 +trente 3 +images 3 +logique 3 +secret 3 +dernières 3 +habitude 3 +zone 3 +faute 3 +faite 3 +surprise 3 +ombre 3 +violence 3 +rend 3 +élèves 3 +blanche 3 +complètement 3 +l’auteur 3 +solution 3 +concernant 3 +biens 3 +notion 3 +viens 3 +suivi 3 +souffle 3 +européenne 3 +capacité 3 +parlé 3 +meilleur 3 +pensées 3 +déjeuner 3 +néanmoins 3 +revanche 3 +jambes 3 +commune 3 +ceci 3 +poste 3 +écrire 3 +international 3 +pièces 3 +tels 3 +sortie 3 +mesures 3 +ferme 3 +poser 3 +distance 3 +rapports 3 +principes 3 +repas 3 +concerne 3 +reprit 3 +habitants 3 +événements 3 +généralement 3 +structure 3 +uns 3 +dame 3 +historique 3 +univers 3 +servir 3 +mètres 3 +influence 3 +nez 3 +autrement 3 +apprendre 3 +apparaît 3 +revenir 3 +extérieur 3 +papier 3 +armes 3 +permettre 3 +œuvres 3 +l’aide 3 +chambres 3 +poids 3 +créer 3 +commission 3 +établissement 3 +évidemment 3 +série 3 +méthode 3 +philosophie 3 +l’existence 3 +salon 3 +utiliser 3 +d’accord 3 +acteurs 3 +l’occasion 3 +sciences 3 +intention 3 +fil 3 +exactement 3 +sinon 3 +château 3 +anciens 3 +décidé 3 +importante 3 +puisqu’ 3 +fonds 3 +demeure 3 +probablement 3 +voulais 3 +quitter 3 +guère 3 +conduit 3 +techniques 3 +assis 3 +empêcher 3 +explique 3 +restaurant 3 +directeur 3 +presse 3 +beauté 3 +association 3 +immédiatement 3 +ouvrir 3 +animaux 3 +récit 3 +certainement 3 +sociaux 3 +avions 3 +procédure 3 +militaire 3 +intérêts 3 +propriété 3 +cheval 3 +référence 3 +villes 3 +retrouve 3 +reçu 3 +sac 3 +frères 3 +garder 3 +défense 3 +campagne 3 +appris 3 +vas 3 +ancienne 3 +sentait 3 +rendu 3 +lentement 3 +espèce 3 +besoins 3 +mourir 3 +continue 3 +noire 3 +film 3 +étranger 3 +auraient 3 +écriture 3 +côte 3 +pourra 3 +agir 3 +difficultés 3 +individu 3 +réel 3 +siège 3 +empire 3 +arrêt 3 +d’entre 3 +lutte 3 +lesquels 3 +unité 3 +envers 3 +prêt 3 +somme 3 +clair 3 +secteur 3 +fonctions 3 +palais 3 +avance 3 +espoir 3 +dimanche 3 +moderne 3 +cesse 3 +marque 3 +constitution 3 +morale 3 +d’autant 3 +conversation 3 +utilisation 3 +oublier 3 +vouloir 3 +statut 3 +rendez 3 +savez 3 +réseau 3 +accueil 3 +connaît 3 +Allemagne 3 +style 3 +projets 3 +divers 3 +fût 3 +royaume 3 +murs 3 +réflexion 3 +neuf 3 +littérature 3 +ouvre 3 +bleu 3 +diverses 3 +totalement 3 +auteurs 3 +d’eux 3 +samedi 3 +morts 3 +définition 2 +sentir 2 +compter 2 +étrange 2 +arrêter 2 +vivant 2 +l’argent 2 +publics 2 +soin 2 +reprendre 2 +port 2 +signifie 2 +contenu 2 +message 2 +ouverture 2 +enquête 2 +fer 2 +tenu 2 +rapide 2 +restait 2 +règle 2 +représentation 2 +essentiel 2 +chaleur 2 +bonnes 2 +su 2 +voyait 2 +familles 2 +l’entrée 2 +nécessité 2 +absolument 2 +évidence 2 +immense 2 +ventre 2 +soirée 2 +grave 2 +veille 2 +traité 2 +internet 2 +revue 2 +soldats 2 +reconnaître 2 +professeur 2 +comportement 2 +couple 2 +doucement 2 +dites 2 +combat 2 +suffit 2 +cru 2 +cherche 2 +individus 2 +tradition 2 +articles 2 +robe 2 +mouvements 2 +conception 2 +commencé 2 +attitude 2 +foule 2 +capitaine 2 +risques 2 +meilleure 2 +responsable 2 +assemblée 2 +d’aller 2 +conséquences 2 +cou 2 +danger 2 +surface 2 +attendait 2 +auquel 2 +recours 2 +tirer 2 +rejoindre 2 +appeler 2 +précisément 2 +arbres 2 +enfance 2 +maisons 2 +sourit 2 +né 2 +fête 2 +l’origine 2 +vente 2 +l’est 2 +vêtements 2 +docteur 2 +rappelle 2 +côtés 2 +dossier 2 +oreille 2 +savais 2 +telles 2 +atteindre 2 +économiques 2 +hier 2 +vaut 2 +amie 2 +mauvaise 2 +écoute 2 +sentiments 2 +lesquelles 2 +portes 2 +seraient 2 +réussi 2 +étions 2 +reine 2 +faux 2 +saison 2 +fleurs 2 +papa 2 +épaule 2 +don 2 +malade 2 +uniquement 2 +répond 2 +ouvrit 2 +connais 2 +soins 2 +souvenirs 2 +forêt 2 +consiste 2 +permettent 2 +reconnaissance 2 +majorité 2 +membre 2 +disposition 2 +opération 2 +rose 2 +objectifs 2 +humanité 2 +types 2 +moments 2 +croissance 2 +dispositions 2 +numéro 2 +jeunesse 2 +clients 2 +poitrine 2 +pages 2 +erreur 2 +recherches 2 +perte 2 +intervention 2 +bons 2 +pouvons 2 +sainte 2 +entretien 2 +sommeil 2 +comprends 2 +parc 2 +édition 2 +supérieur 2 +effort 2 +hôpital 2 +phrase 2 +debout 2 +dîner 2 +bébé 2 +limites 2 +sentit 2 +pouvaient 2 +noms 2 +élément 2 +faveur 2 +jugement 2 +l’expérience 2 +ouverte 2 +viennent 2 +gorge 2 +musée 2 +aspect 2 +agissait 2 +l’armée 2 +l’être 2 +pire 2 +siècles 2 +internationale 2 +appartement 2 +revient 2 +moyenne 2 +portée 2 +aimait 2 +représente 2 +tombe 2 +chien 2 +découverte 2 +personnage 2 +d’y 2 +arrête 2 +juifs 2 +portait 2 +lignes 2 +naturel 2 +fonctionnement 2 +l’œuvre 2 +basse 2 +appelé 2 +Londres 2 +laissa 2 +compétences 2 +éditions 2 +largement 2 +épouse 2 +hasard 2 +venue 2 +ministère 2 +couleurs 2 +total 2 +démarche 2 +passion 2 +capital 2 +masse 2 +d’œil 2 +payer 2 +seuil 2 +comprend 2 +tourna 2 +pain 2 +documents 2 +passant 2 +vécu 2 +l’âge 2 +faisaient 2 +perspective 2 +hauteur 2 +riche 2 +photo 2 +lundi 2 +odeur 2 +l’expression 2 +fruits 2 +dents 2 +institutions 2 +évaluation 2 +bref 2 +recevoir 2 +protéger 2 +l’absence 2 +passa 2 +dimension 2 +comte 2 +permettant 2 +tiers 2 +sujets 2 +sexe 2 +scientifique 2 +juridique 2 +humains 2 +camp 2 +opinion 2 +convention 2 +principal 2 +sortit 2 +réaliser 2 +duc 2 +notes 2 +courage 2 +comptes 2 +fera 2 +animal 2 +situations 2 +douce 2 +équilibre 2 +entier 2 +pouvoirs 2 +difficulté 2 +portant 2 +dessous 2 +concept 2 +mondiale 2 +l’âme 2 +fou 2 +nécessaires 2 +sources 2 +noirs 2 +circonstances 2 +religieux 2 +comité 2 +spectacle 2 +vitesse 2 +tuer 2 +choisi 2 +pont 2 +prenant 2 +voyant 2 +échange 2 +monter 2 +phénomène 2 +ajouta 2 +tendance 2 +victime 2 +exécution 2 +présenter 2 +sombre 2 +hiver 2 +pression 2 +passait 2 +laissant 2 +patient 2 +privé 2 +beaux 2 +section 2 +traits 2 +résistance 2 +facilement 2 +défaut 2 +suivante 2 +procès 2 +intelligence 2 +bataille 2 +échapper 2 +soutien 2 +commencer 2 +l’instant 2 +disant 2 +légèrement 2 +finit 2 +opérations 2 +Angleterre 2 +vraie 2 +métier 2 +liens 2 +réaction 2 +autorités 2 +faudrait 2 +efforts 2 +jeux 2 +clairement 2 +vint 2 +l’organisation 2 +chair 2 +accepter 2 +pouvais 2 +changé 2 +change 2 +cerveau 2 +auparavant 2 +sert 2 +espère 2 +revenu 2 +l’effet 2 +Italie 2 +choisir 2 +obligation 2 +événement 2 +l’Europe 2 +devenue 2 +belles 2 +donnait 2 +rentrer 2 +progrès 2 +chacune 2 +matériel 2 +plat 2 +prison 2 +bar 2 +formule 2 +connaissait 2 +poche 2 +entièrement 2 +capitale 2 +quinze 2 +profonde 2 +aimé 2 +systèmes 2 +opposition 2 +continuer 2 +banque 2 +pensé 2 +vaste 2 +conséquence 2 +disparu 2 +secondes 2 +l’île 2 +jusque 2 +suffisamment 2 +vert 2 +justement 2 +conduite 2 +stratégie 2 +célèbre 2 +centrale 2 +rythme 2 +personnages 2 +êtres 2 +réserve 2 +ajouter 2 +tribunal 2 +murmura 2 +sérieux 2 +longues 2 +atteint 2 +claire 2 +consommation 2 +parlait 2 +profondément 2 +publiques 2 +l’affaire 2 +décisions 2 +remettre 2 +agréable 2 +profit 2 +convient 2 +gagner 2 +chargé 2 +entière 2 +limite 2 +tiens 2 +imaginer 2 +classique 2 +échelle 2 +précise 2 +possède 2 +arbre 2 +terres 2 +tâche 2 +connaissances 2 +escalier 2 +euros 2 +jeta 2 +détails 2 +profond 2 +complexe 2 +essayer 2 +interne 2 +parfait 2 +ennemi 2 +séjour 2 +sent 2 +genoux 2 +montagne 2 +lune 2 +put 2 +préparer 2 +l’intérêt 2 +tourner 2 +établir 2 +étrangers 2 +professionnelle 2 +quelles 2 +chaud 2 +rues 2 +doux 2 +conflit 2 +interprétation 2 +proches 2 +cinéma 2 +élève 2 +plaît 2 +nul 2 +développer 2 +nouvel 2 +boire 2 +civile 2 +d’argent 2 +attend 2 +examen 2 +plage 2 +oublié 2 +utile 2 +pointe 2 +particulière 2 +signes 2 +exception 2 +prévu 2 +rares 2 +terrasse 2 +empereur 2 +retourner 2 +donnée 2 +ordres 2 +offrir 2 +cri 2 +régulièrement 2 +occuper 2 +boîte 2 +carrière 2 +l’avenir 2 +étape 2 +posa 2 +rappeler 2 +vertu 2 +Amérique 2 +eaux 2 +trouvent 2 +suppose 2 +posé 2 +pauvres 2 +régions 2 +doigt 2 +triste 2 +prête 2 +francs 2 +client 2 +autrefois 2 +outils 2 +populaire 2 +d’or 2 +Chine 2 +phase 2 +situé 2 +construire 2 +conseils 2 +prends 2 +salut 2 +aimer 2 +Espagne 2 +cinquante 2 +ferait 2 +conduire 2 +cents 2 +l’analyse 2 +naturellement 2 +prises 2 +réellement 2 +l’activité 2 +déclaration 2 +suit 2 +héros 2 +l’économie 2 +nation 2 +multiples 2 +extrême 2 +huile 2 +l’université 2 +lever 2 +précis 2 +réunion 2 +collection 2 +ordinaire 2 +élevé 2 +étage 2 +croix 2 +vendredi 2 +cercle 2 +pluie 2 +principale 2 +lança 2 +blancs 2 +voyez 2 +engagement 2 +montant 2 +vacances 2 +naturelle 2 +entend 2 +bibliothèque 2 +volume 2 +hypothèse 2 +d’État 2 +tente 2 +étudiants 2 +américain 2 +langues 2 +exprimer 2 +rencontrer 2 +chefs 2 +créé 2 +quels 2 +émotions 2 +partage 2 +parcours 2 +champs 2 +fortune 2 +idéal 2 +exploitation 2 +surpris 2 +feuilles 2 +titres 2 +proposition 2 +émotion 2 +nations 2 +heureuse 2 +pourraient 2 +victimes 2 +victoire 2 +quantité 2 +écran 2 +souffrance 2 +version 2 +kilomètres 2 +pourrais 2 +d’origine 2 +l’évolution 2 +remarque 2 +caractéristiques 2 +locaux 2 +allemand 2 +produire 2 +observer 2 +traduction 2 +acheter 2 +incapable 2 +attendant 2 +assurance 2 +bateau 2 +faudra 2 +futur 2 +militaires 2 +terrible 2 +apprentissage 2 +l’endroit 2 +essentiellement 2 +oncle 2 +annonce 2 +professionnel 2 +revoir 2 +arrêté 2 +proximité 2 +amoureux 2 +esprits 2 +principaux 2 +relève 2 +prenait 2 +document 2 +facteurs 2 +peuples 2 +méthodes 2 +arme 2 +l’hôtel 2 +regards 2 +oreilles 2 +partis 2 +participation 2 +indépendance 2 +réfléchir 2 +bâtiment 2 +guide 2 +baiser 2 +considérer 2 +exemples 2 +professionnels 2 +l’importance 2 +agents 2 +conclusion 2 +lac 2 +correspond 2 +simples 2 +crime 2 +l’Union 2 +secrétaire 2 +conséquent 2 +neige 2 +devaient 2 +utilisé 2 +lait 2 +photos 2 +fasse 2 +zones 2 +ajoute 2 +attente 2 +dormir 2 +donna 2 +finir 2 +réalisation 2 +pensait 2 +réponses 2 +quarante 2 +chevaux 2 +toucher 2 +totale 2 +vague 2 +sûre 2 +regardait 2 +apporter 2 +tenue 2 +magnifique 2 +lecteur 2 +montré 2 +léger 2 +appareil 2 +l’autorité 2 +troubles 2 +commença 2 +retraite 2 +degré 2 +quotidien 2 +supérieure 2 +tenter 2 +dur 2 +sommet 2 +humaines 2 +civil 2 +découvert 2 +démocratie 2 +plats 2 +rivière 2 +considéré 2 +allemands 2 +populations 2 +douze 2 +fermé 2 +personnelle 2 +fortement 2 +débat 2 +sûrement 2 +chaîne 2 +sensation 2 +aspects 2 +chaise 2 +constituent 2 +veulent 2 +machine 2 +espèces 2 +différent 2 +touche 2 +propriétaire 2 +temple 2 +poète 2 +jeter 2 +donnant 2 +l’ombre 2 +européen 2 +dieux 2 +efficacité 2 +paraissait 2 +patients 2 +crédit 2 +central 2 +chasse 2 +destin 2 +gris 2 +voudrais 2 +introduction 2 +amitié 2 +personnalité 2 +possibles 2 +relative 2 +normal 2 +cartes 2 +brusquement 2 +délai 2 +office 2 +tourne 2 +efficace 2 +histoires 2 +regardant 2 +donnent 2 +chute 2 +détail 2 +laissez 2 +agent 2 +coût 2 +expériences 2 +marchés 2 +énorme 2 +aventure 2 +adulte 2 +dispose 2 +gare 2 +l’œil 2 +local 2 +troupes 2 +trouva 2 +semblent 2 +alla 2 +industrie 2 +inutile 2 +arrêta 2 +structures 2 +resta 2 +mérite 2 +l’étude 2 +réforme 2 +arrivait 2 +coupe 2 +secours 2 +menace 2 +couloir 2 +revenus 2 +privée 2 +religieuse 2 +cris 2 +judiciaire 2 +défendre 2 +institution 2 +d’amour 2 +gloire 2 +écria 2 +garçons 2 +citoyens 2 +écouter 2 +éternel 2 +raconte 2 +quitté 2 +repris 2 +rouges 2 +sauver 2 +lève 2 +l’administration 2 +l’intention 2 +compétence 2 +patron 2 +dialogue 2 +faim 2 +américains 2 +l’égard 2 +attaque 2 +issue 2 +accident 2 +course 2 +arts 2 +partager 2 +l’exercice 2 +discussion 2 +catégorie 2 +concurrence 2 +traduit 2 +lu 2 +assise 2 +contrairement 2 +envoyé 2 +souviens 2 +coucher 2 +morte 2 +effectivement 2 +réelle 2 +l’Assemblée 2 +rupture 2 +ouvrages 2 +heureusement 2 +nourriture 2 +l’extérieur 2 +restent 2 +saisir 2 +littéraire 2 +sois 2 +di 2 +représentant 2 +l’éducation 2 +gaz 2 +réseaux 2 +épreuve 2 +lance 2 +rêves 2 +remarquer 2 +croit 2 +couverture 2 +honte 2 +paysage 2 +marcher 2 +l’environnement 2 +courir 2 +rare 2 +l’Empire 2 +domaines 2 +construit 2 +artiste 2 +pouvant 2 +estime 2 +milliers 2 +établissements 2 +imagination 2 +classes 2 +dynamique 2 +répondu 2 +réduire 2 +retourna 2 +interdit 2 +échec 2 +patrimoine 2 +indique 2 +serais 2 +appartient 2 +avantage 2 +soixante 2 +génération 2 +souci 2 +échanges 2 +archives 2 +présentation 1 +prennent 1 +impact 1 +modèles 1 +plans 1 +diable 1 +bel 1 +crainte 1 +minute 1 +pierres 1 +jolie 1 +aise 1 +malheureusement 1 +os 1 +instruction 1 +vive 1 +salariés 1 +égalité 1 +considère 1 +clé 1 +sable 1 +augmentation 1 +écart 1 +devons 1 +thème 1 +saurait 1 +avocat 1 +principalement 1 +l’enseignement 1 +asseoir 1 +apparition 1 +compréhension 1 +importants 1 +longs 1 +époux 1 +préparation 1 +déclara 1 +sourcils 1 +règne 1 +grosse 1 +trace 1 +puissant 1 +repos 1 +règlement 1 +collective 1 +critères 1 +écoles 1 +fauteuil 1 +prie 1 +nommé 1 +organisations 1 +raconter 1 +observation 1 +espaces 1 +aient 1 +danse 1 +l’acte 1 +club 1 +gestes 1 +rang 1 +maintenir 1 +explication 1 +transport 1 +angoisse 1 +profiter 1 +niveaux 1 +inverse 1 +sensible 1 +repose 1 +occupe 1 +bus 1 +bande 1 +haine 1 +transformation 1 +tué 1 +l’hôpital 1 +impose 1 +tension 1 +dépenses 1 +instants 1 +disent 1 +l’usage 1 +d’ici 1 +intégration 1 +paraître 1 +porté 1 +peinture 1 +humeur 1 +réduit 1 +allaient 1 +mine 1 +pure 1 +retenir 1 +sites 1 +poursuivre 1 +cacher 1 +normes 1 +thé 1 +forcément 1 +dirait 1 +angle 1 +complète 1 +vérifier 1 +Israël 1 +imagine 1 +publié 1 +intéressant 1 +relativement 1 +venus 1 +liés 1 +horizon 1 +nulle 1 +l’attention 1 +conflits 1 +reprises 1 +soumis 1 +thèse 1 +communes 1 +battre 1 +l’humanité 1 +importantes 1 +bête 1 +décide 1 +changements 1 +printemps 1 +appelait 1 +catholique 1 +médecine 1 +manifeste 1 +riches 1 +exposition 1 +charme 1 +catégories 1 +matières 1 +ennemis 1 +Russie 1 +glace 1 +refus 1 +écrivain 1 +envoyer 1 +locale 1 +mardi 1 +télévision 1 +l’ouest 1 +travaille 1 +l’avoir 1 +apparence 1 +scolaire 1 +malheur 1 +allais 1 +chère 1 +l’information 1 +locales 1 +proposer 1 +Noël 1 +rarement 1 +jaune 1 +spécifique 1 +dépend 1 +tranquille 1 +artistes 1 +rencontré 1 +poésie 1 +écrits 1 +traces 1 +définir 1 +oiseaux 1 +fixe 1 +choc 1 +del 1 +américaine 1 +vigueur 1 +différente 1 +véhicule 1 +chinois 1 +parlement 1 +allée 1 +autonomie 1 +décrit 1 +installer 1 +miroir 1 +maîtresse 1 +essence 1 +l’équipe 1 +douceur 1 +maîtres 1 +dépit 1 +déterminer 1 +distinction 1 +vallée 1 +causes 1 +l’arrivée 1 +resté 1 +chemise 1 +ost 1 +attends 1 +inconnu 1 +menu 1 +radio 1 +boutique 1 +l’ont 1 +qualités 1 +mener 1 +sucre 1 +apparemment 1 +quasi 1 +cabinet 1 +tendre 1 +joues 1 +satisfaction 1 +reconnu 1 +montagnes 1 +mélange 1 +travailleurs 1 +responsables 1 +atmosphère 1 +programmes 1 +financière 1 +nécessairement 1 +diversité 1 +éthique 1 +frontière 1 +pensais 1 +tendit 1 +meilleurs 1 +département 1 +obligations 1 +indispensable 1 +retrouvé 1 +désert 1 +saisit 1 +descendre 1 +prière 1 +entendit 1 +constater 1 +extrêmement 1 +assure 1 +avion 1 +convaincre 1 +officier 1 +distinguer 1 +seules 1 +aimerais 1 +chrétiens 1 +comportements 1 +sortes 1 +séance 1 +ambiance 1 +évident 1 +sœurs 1 +officiers 1 +commandant 1 +dangereux 1 +acquis 1 +proposé 1 +génie 1 +quitte 1 +province 1 +viande 1 +tend 1 +apparaître 1 +charges 1 +tort 1 +description 1 +tantôt 1 +disparaître 1 +entends 1 +via 1 +urgence 1 +outil 1 +médecins 1 +liée 1 +sienne 1 +servi 1 +représentations 1 +inquiète 1 +complet 1 +commissaire 1 +prêtre 1 +appui 1 +trait 1 +tenant 1 +maîtrise 1 +actuellement 1 +ouvriers 1 +actuelle 1 +souhaite 1 +l’énergie 1 +identifier 1 +obligé 1 +libération 1 +mystère 1 +plantes 1 +folie 1 +dollars 1 +tante 1 +moral 1 +ange 1 +trouble 1 +perception 1 +sachant 1 +journaux 1 +alcool 1 +mettent 1 +sorti 1 +arabe 1 +surveillance 1 +pardon 1 +européens 1 +commençait 1 +vienne 1 +cellules 1 +mises 1 +abri 1 +d’années 1 +digne 1 +alliance 1 +établi 1 +devint 1 +dure 1 +commande 1 +pur 1 +décret 1 +scientifiques 1 +direct 1 +doctrine 1 +morceau 1 +intérieure 1 +dedans 1 +parfaite 1 +tournant 1 +séparation 1 +associations 1 +inscrit 1 +laissait 1 +étoiles 1 +fleuve 1 +l’univers 1 +initiative 1 +curiosité 1 +pensez 1 +monte 1 +poussa 1 +remise 1 +concours 1 +réduction 1 +serai 1 +culturelle 1 +l’ancien 1 +retard 1 +signification 1 +suisse 1 +essaie 1 +mademoiselle 1 +progressivement 1 +investissement 1 +médias 1 +principales 1 +pareil 1 +russe 1 +coopération 1 +collectif 1 +savent 1 +refuse 1 +sagesse 1 +prenez 1 +bonjour 1 +sel 1 +récemment 1 +royal 1 +films 1 +circulation 1 +promis 1 +capacités 1 +adultes 1 +Canada 1 +poisson 1 +fins 1 +pousse 1 +l’emploi 1 +divine 1 +empêche 1 +l’utilisation 1 +voies 1 +lié 1 +couvert 1 +immeuble 1 +fenêtres 1 +réalisé 1 +accent 1 +liées 1 +commis 1 +quête 1 +fruit 1 +moteur 1 +passée 1 +améliorer 1 +longueur 1 +portrait 1 +ressemble 1 +familiale 1 +bouteille 1 +venaient 1 +demandait 1 +distribution 1 +firent 1 +reprend 1 +organiser 1 +modes 1 +disparition 1 +l’individu 1 +quatrième 1 +implique 1 +pape 1 +conférence 1 +voyons 1 +modalités 1 +parut 1 +continua 1 +der 1 +extraordinaire 1 +essayé 1 +atteinte 1 +revint 1 +située 1 +van 1 +croyait 1 +malheureux 1 +frontières 1 +culturel 1 +tours 1 +connue 1 +placé 1 +spécifiques 1 +révèle 1 +mettant 1 +contient 1 +coupable 1 +bain 1 +cycle 1 +souriant 1 +suivantes 1 +voisins 1 +contrats 1 +essai 1 +traiter 1 +civilisation 1 +différences 1 +françaises 1 +curieux 1 +tourisme 1 +retourne 1 +ordinateur 1 +princesse 1 +maximum 1 +observe 1 +l’établissement 1 +auxquels 1 +cliquez 1 +témoin 1 +tenté 1 +accepté 1 +l’oreille 1 +composé 1 +accompagner 1 +avancer 1 +tire 1 +dispositif 1 +réception 1 +dirigeants 1 +institut 1 +avantages 1 +bains 1 +trou 1 +inspiration 1 +financiers 1 +collègues 1 +agence 1 +horreur 1 +soutenir 1 +communautés 1 +fondée 1 +sacré 1 +arriva 1 +suivants 1 +froide 1 +piste 1 +stade 1 +clinique 1 +solutions 1 +sage 1 +atelier 1 +richesse 1 +respecter 1 +composition 1 +anciennes 1 +terminé 1 +devez 1 +exercer 1 +relever 1 +bilan 1 +transfert 1 +désigne 1 +agriculture 1 +fallu 1 +étendue 1 +maladies 1 +domicile 1 +exprime 1 +allé 1 +former 1 +sport 1 +publication 1 +division 1 +abbé 1 +planète 1 +parvenir 1 +centaines 1 +clés 1 +architecture 1 +participer 1 +nuits 1 +l’Afrique 1 +figures 1 +fidèle 1 +Orient 1 +couche 1 +utilise 1 +résoudre 1 +vif 1 +queue 1 +quelconque 1 +l’arrière 1 +l’étranger 1 +symbolique 1 +calcul 1 +monnaie 1 +feuille 1 +affirme 1 +chrétienne 1 +l’été 1 +duquel 1 +phénomènes 1 +présenté 1 +avenue 1 +chat 1 +rendit 1 +cultures 1 +présents 1 +obscurité 1 +gardes 1 +d’action 1 +réputation 1 +température 1 +légère 1 +joué 1 +enjeux 1 +sauvage 1 +conserver 1 +répliqua 1 +rois 1 +rendait 1 +culte 1 +instrument 1 +prévenir 1 +employés 1 +juger 1 +plateau 1 +voitures 1 +glisser 1 +gérer 1 +imposer 1 +destinée 1 +courte 1 +sec 1 +baisse 1 +visible 1 +prêts 1 +chiens 1 +lancer 1 +historiques 1 +imaginaire 1 +fine 1 +marine 1 +dite 1 +d’intérêt 1 +obtenu 1 +lumières 1 +étapes 1 +océan 1 +soyez 1 +critiques 1 +toile 1 +Bretagne 1 +drôle 1 +comparaison 1 +vierge 1 +résolution 1 +décider 1 +préfère 1 +paiement 1 +flux 1 +secrets 1 +rage 1 +islam 1 +budget 1 +approcha 1 +secoua 1 +épée 1 +enceinte 1 +noter 1 +croyez 1 +installé 1 +l’écriture 1 +solitude 1 +lieutenant 1 +réussite 1 +collège 1 +hélas 1 +l’exemple 1 +fournir 1 +l’escalier 1 +pleurer 1 +découvre 1 +aurez 1 +net 1 +vendre 1 +directe 1 +l’application 1 +entra 1 +autrui 1 +quoique 1 +minimum 1 +intéresse 1 +voisin 1 +motif 1 +soupir 1 +intermédiaire 1 +engager 1 +prochaine 1 +morceaux 1 +sien 1 +l’essentiel 1 +durable 1 +finances 1 +allemande 1 +folle 1 +reçoit 1 +lourd 1 +autorisation 1 +fatigue 1 +l’honneur 1 +instruments 1 +étrangère 1 +toit 1 +précieux 1 +possibilités 1 +cuir 1 +marches 1 +précédent 1 +inquiétude 1 +remarqué 1 +cessé 1 +allant 1 +représentants 1 +appliquer 1 +actuel 1 +devrais 1 +légumes 1 +modernes 1 +îles 1 +numérique 1 +quartiers 1 +possession 1 +tiré 1 +portent 1 +fameux 1 +discuter 1 +origines 1 +procédures 1 +comporte 1 +respiration 1 +maire 1 +financement 1 +témoignage 1 +parlant 1 +villages 1 +ignore 1 +absolue 1 +pêche 1 +montrent 1 +territoires 1 +actif 1 +éclat 1 +généraux 1 +organisme 1 +chiffres 1 +deviennent 1 +étrangères 1 +tâches 1 +tentative 1 +intensité 1 +pâle 1 +distingue 1 +voile 1 +relatives 1 +élections 1 +pitié 1 +mettait 1 +quart 1 +foyer 1 +nationales 1 +l’habitude 1 +venez 1 +attendu 1 +luxe 1 +internationales 1 +pourrez 1 +soupe 1 +mets 1 +physiques 1 +devais 1 +religieuses 1 +l’objectif 1 +mémoires 1 +cria 1 +liquide 1 +décida 1 +tissu 1 +chanson 1 +tristesse 1 +colonel 1 +tableaux 1 +Bruxelles 1 +associés 1 +apporte 1 +fondé 1 +orientation 1 +chevalier 1 +vieil 1 +portable 1 +libres 1 +dut 1 +voulut 1 +nu 1 +décès 1 +conseiller 1 +Algérie 1 +l’influence 1 +compagnons 1 +remis 1 +verbe 1 +diffusion 1 +renseignements 1 +branches 1 +abandonner 1 +fleur 1 +faculté 1 +l’identité 1 +vins 1 +accompagné 1 +centres 1 +accéder 1 +l’unité 1 +royale 1 +l’Allemagne 1 +dimensions 1 +suprême 1 +crée 1 +assistance 1 +exigences 1 +souffrir 1 +chant 1 +transformer 1 +achat 1 +milliards 1 +cellule 1 +enseignants 1 +pousser 1 +voyages 1 +crâne 1 +reprise 1 +d’hommes 1 +solide 1 +tables 1 +facteur 1 +malades 1 +posée 1 +financier 1 +soldat 1 +coûts 1 +allure 1 +vivent 1 +destination 1 +auront 1 +venant 1 +intime 1 +couteau 1 +poussière 1 +intense 1 +aperçut 1 +visiter 1 +bière 1 +climat 1 +romain 1 +définitivement 1 +d’esprit 1 +talent 1 +l’accès 1 +Inde 1 +volontiers 1 +papiers 1 +étudier 1 +semblaient 1 +merde 1 +juges 1 +enfer 1 +entendait 1 +désolé 1 +magasin 1 +erreurs 1 +l’après 1 +manteau 1 +latin 1 +muscles 1 +with 1 +plafond 1 +quelquefois 1 +fuite 1 +bâtiments 1 +jette 1 +excès 1 +unes 1 +constitué 1 +chapeau 1 +accepte 1 +journaliste 1 +placer 1 +marqué 1 +chiffre 1 +certitude 1 +l’aise 1 +comprit 1 +solidarité 1 +disponible 1 +visiblement 1 +passent 1 +profondeur 1 +vagues 1 +herbe 1 +stratégies 1 +difficiles 1 +final 1 +levant 1 +impôt 1 +poissons 1 +hautes 1 +particuliers 1 +associé 1 +station 1 +philosophe 1 +diagnostic 1 +embrasser 1 +contribution 1 +innovation 1 +loup 1 +serra 1 +race 1 +démocratique 1 +capables 1 +compagnon 1 +intellectuelle 1 +propositions 1 +auxquelles 1 +chaussures 1 +savons 1 +magie 1 +guerres 1 +l’ouverture 1 +supporter 1 +semblable 1 +motifs 1 +d’art 1 +phrases 1 +logement 1 +entraîne 1 +trouvaient 1 +transmission 1 +tas 1 +illusion 1 +reconnaît 1 +chemins 1 +croyais 1 +profession 1 +discipline 1 +endroits 1 +admettre 1 +désespoir 1 +récits 1 +garantie 1 +armées 1 +d’affaires 1 +contente 1 +pensa 1 +vain 1 +gars 1 +intervenir 1 +justifier 1 +paradis 1 +symbole 1 +cherchait 1 +affirmer 1 +faiblesse 1 +expliqua 1 +attirer 1 +fumée 1 +décor 1 +pantalon 1 +col 1 +contraintes 1 +excellent 1 +appelée 1 +gratuit 1 +active 1 +l’espoir 1 +messieurs 1 +écho 1 +présentent 1 +prochain 1 +dette 1 +succession 1 +auto 1 +correspondance 1 +performance 1 +crim 1 +contrôler 1 +effectuer 1 +d’entrée 1 +née 1 +cache 1 +universelle 1 +d’information 1 +dommage 1 +préféré 1 +perdue 1 +promesse 1 +enthousiasme 1 +camarades 1 +concentration 1 +têtes 1 +salaire 1 +hauts 1 +détermination 1 +totalité 1 +souris 1 +résidence 1 +débats 1 +résulte 1 +employé 1 +traditionnelle 1 +esthétique 1 +menton 1 +chercheurs 1 +remplir 1 +restauration 1 +accueillir 1 +silhouette 1 +absolu 1 +d’ordre 1 +chrétien 1 +jus 1 +visites 1 +remonter 1 +générations 1 +propriétés 1 +trajet 1 +crème 1 +hein 1 +chocolat 1 +grec 1 +sélection 1 +vice 1 +jeudi 1 +évoque 1 +décrire 1 +l’écart 1 +terreur 1 +considérée 1 +lycée 1 +dessin 1 +nettement 1 +dignité 1 +correspondant 1 +renvoie 1 +retirer 1 +interroger 1 +truc 1 +l’ouvrage 1 +pause 1 +manières 1 +Asie 1 +l’Etat 1 +vivement 1 +ressemblait 1 +affection 1 +constituer 1 +approcher 1 +dizaine 1 +chaude 1 +occupé 1 +réflexions 1 +adaptation 1 +potentiel 1 +meurtre 1 +vise 1 +poursuit 1 +mœurs 1 +lueur 1 +héritage 1 +navire 1 +utilisée 1 +pauvreté 1 +tapis 1 +poursuivit 1 +âmes 1 +utilisés 1 +promotion 1 +noble 1 +jurisprudence 1 +accomplir 1 +évêque 1 +congrès 1 +creux 1 +musulmans 1 +préciser 1 +hâte 1 +confusion 1 +témoins 1 +trésor 1 +manifestations 1 +l’avons 1 +rejoint 1 +existait 1 +gouverneur 1 +canapé 1 +amant 1 +l’opinion 1 +l’épaule 1 +l’avais 1 +d’énergie 1 +l’écran 1 +l’empereur 1 +l’échelle 1 +l’opération 1 +l’avant 1 +l’ennemi 1 +l’exécution 1 +l’industrie 1 +d’habitude 1 +l’odeur 1 +l’obligation 1 +l’avaient 1 +d’enfants 1 +l’ancienne 1 +l’abri 1 +l’obscurité 1 +l’approche 1 +l’appartement 1 +l’horizon 1 +d’obtenir 1 +l’enquête 1 +l’inverse 1 +d’entrer 1 +l’appareil 1 +l’intervention 1 +l’équilibre 1 +l’abbé 1 +d’éviter 1 +d’informations 1 +d’histoire 1 +l’association 1 +l’intelligence 1 +l’arrêt 1 +l’hiver 1 +l’exception 1 +l’animal 1 +l’aspect 1 +l’hypothèse 1 +l’accord 1 +l’atmosphère 1 +d’études 1 +l’opposition 1 +l’appel 1 +l’envie 1 +l’indépendance 1 +d’accueil 1 +l’as 1 +d’entreprise 1 +d’honneur 1 +l’évaluation 1 +l’aube 1 +l’épreuve 1 +d’air 1 +l’adresse 1 +l’apparition 1 +l’égalité 1 +l’aurait 1 +l’examen 1 +l’efficacité 1 +d’appel 1 +d’agir 1 +d’elles 1 +l’Angleterre 1 +l’attitude 1 +l’accueil 1 +d’apprendre 1 +d’activité 1 +l’enfance 1 +l’aider 1 +l’événement 1 +l’offre 1 +d’accès 1 +l’interprétation 1 +l’effort 1 +l’Italie 1 +d’écrire 1 +l’exploitation 1 +l’artiste 1 +l’agriculture 1 +l’entretien 1 +l’institution 1 +l’impact 1 +l’arbre 1 +l’an 1 +l’espèce 1 +l’océan 1 +d’homme 1 +l’huile 1 +l’accent 1 +d’importance 1 +d’entendre 1 +l’alcool 1 +d’arriver 1 +l’Amérique 1 +l’engagement 1 +l’angle 1 +l’avez 1 +l’initiative 1 +d’armes 1 +l’évidence 1 +d’utiliser 1 +l’or 1 +d’emploi 1 +l’imagination 1 +l’échec 1 +l’Éternel 1 +l’apprentissage 1 +l’aéroport 1 +l’issue 1 +l’émotion 1 +l’agent 1 +l’écrivain 1 +d’Europe 1 +l’islam 1 +d’application 1 +l’ambiance 1 +l’intégration 1 +d’aide 1 +l’Espagne 1 +d’Israël 1 +l’herbe 1 +d’avance 1 +l’étage 1 +l’avantage 1 +l’essence 1 +l’employeur 1 +d’assurer 1 +l’angoisse 1 +l’élaboration 1 +d’ouvrir 1 +d’établir 1 +l’unique 1 +d’expression 1 +d’analyse 1 +l’immeuble 1 +l’émergence 1 +l’Académie 1 +d’emblée 1 \ No newline at end of file diff --git a/lang/la/wordlist.txt b/lang/la/wordlist.txt new file mode 100644 index 0000000..453cfa6 --- /dev/null +++ b/lang/la/wordlist.txt @@ -0,0 +1,1577 @@ +et 197 +in 142 +est 100 +non 91 +ut 72 +cum 62 +si 61 +ad 59 +quod 54 +qui 47 +sed 42 +quae 38 +ex 37 +quam 35 +esse 33 +de 31 +aut 31 +a 30 +hoc 27 +nec 26 +sunt 23 +etiam 23 +se 23 +quid 22 +enim 22 +ab 21 +per 21 +sit 21 +atque 20 +id 19 +autem 19 +quo 18 +uel 18 +me 18 +ne 17 +te 17 +ac 17 +nam 17 +tamen 16 +eius 15 +haec 15 +mihi 15 +ita 15 +iam 15 +neque 14 +quidem 13 +eo 13 +quoque 13 +ea 12 +pro 12 +tibi 12 +quia 12 +ego 12 +nihil 11 +eum 11 +modo 11 +nunc 11 +sic 10 +libro 10 +an 10 +quem 10 +quibus 10 +inter 10 +qua 10 +esset 10 +causa 10 +erat 10 +nisi 9 +hic 9 +potest 9 +uero 9 +tum 9 +quis 9 +ipse 9 +fuit 9 +tu 9 +ille 9 +ante 9 +sine 9 +res 9 +his 8 +omnia 8 +idem 8 +ubi 8 +sibi 8 +illa 7 +post 7 +rem 7 +ei 7 +tam 7 +apud 7 +tantum 7 +magis 7 +at 6 +erit 6 +deinde 6 +quos 6 +cui 6 +omnes 6 +is 6 +re 6 +contra 6 +nos 6 +cuius 6 +omnibus 6 +minus 6 +quasi 6 +ergo 5 +eam 5 +igitur 5 +rei 5 +sub 5 +posse 5 +dum 5 +eorum 5 +sua 5 +inquit 5 +itaque 5 +sint 5 +primum 5 +ipsa 5 +habet 5 +illud 5 +suo 5 +item 5 +eos 5 +illi 5 +siue 5 +satis 5 +nobis 5 +parte 5 +hanc 5 +ait 5 +rerum 5 +semper 5 +propter 5 +tempore 5 +loco 4 +possit 4 +unde 4 +rebus 4 +fuerit 4 +inde 4 +omnis 4 +omnium 4 +quoniam 4 +fieri 4 +eadem 4 +nomine 4 +alia 4 +maxime 4 +hunc 4 +alii 4 +hac 4 +pater 4 +quas 4 +facere 4 +saepe 4 +uerum 4 +aliquid 4 +suis 4 +bene 4 +ipsum 4 +die 4 +mea 4 +multa 4 +nomen 4 +solum 4 +uidetur 4 +illum 4 +unum 4 +fuisse 4 +nulla 4 +natura 4 +primo 4 +simul 4 +ob 4 +una 4 +dies 4 +postea 4 +quidam 3 +factum 3 +habere 3 +senatus 3 +tempus 3 +iis 3 +uos 3 +tunc 3 +dixit 3 +licet 3 +tua 3 +iure 3 +quantum 3 +dicere 3 +uti 3 +bellum 3 +dicitur 3 +partem 3 +genus 3 +numquam 3 +ideo 3 +locum 3 +ibi 3 +pars 3 +aliud 3 +eodem 3 +quorum 3 +huius 3 +erant 3 +aduersus 3 +filius 3 +sum 3 +nemo 3 +suum 3 +debet 3 +animo 3 +hominum 3 +supra 3 +opus 3 +sui 3 +causam 3 +dicit 3 +hinc 3 +quin 3 +uis 3 +magna 3 +fecit 3 +sicut 3 +plus 3 +illo 3 +ius 3 +o 3 +edictum 3 +heres 3 +arma 3 +ista 3 +illis 3 +ipsi 3 +fit 3 +suam 3 +huic 3 +uerba 3 +essent 3 +homines 3 +duo 3 +facit 3 +facta 3 +usque 3 +manus 3 +cur 3 +quamuis 3 +quaedam 3 +potius 3 +ipso 3 +omni 3 +dedit 3 +usus 3 +animi 3 +uenit 3 +diem 3 +populi 3 +urbem 3 +castra 3 +romani 2 +genere 2 +manu 2 +haud 2 +prima 2 +bello 2 +uno 2 +summa 2 +forte 2 +ui 2 +aliis 2 +ratio 2 +prius 2 +ratione 2 +posset 2 +adhuc 2 +certe 2 +tamquam 2 +publicae 2 +terra 2 +filium 2 +nostra 2 +publica 2 +bona 2 +circa 2 +es 2 +meo 2 +aqua 2 +caput 2 +uerbis 2 +lege 2 +super 2 +denique 2 +fortuna 2 +illam 2 +uelut 2 +quamquam 2 +possunt 2 +sane 2 +secundum 2 +oportet 2 +filio 2 +hominem 2 +praeter 2 +patris 2 +uobis 2 +recte 2 +modum 2 +armis 2 +belli 2 +suae 2 +pecuniam 2 +tot 2 +uita 2 +necesse 2 +meum 2 +habent 2 +corpus 2 +aliter 2 +caesar 2 +suos 2 +multis 2 +utrum 2 +diu 2 +uitae 2 +dare 2 +hostium 2 +paulus 2 +mortem 2 +praeterea 2 +uix 2 +animum 2 +postquam 2 +alio 2 +omne 2 +statim 2 +totum 2 +milia 2 +dictum 2 +uideri 2 +adeo 2 +intra 2 +fere 2 +uim 2 +tanta 2 +alter 2 +mox 2 +alterum 2 +partes 2 +umquam 2 +patrem 2 +facile 2 +multo 2 +iudicium 2 +iudices 2 +domum 2 +ulpianus 2 +scilicet 2 +tota 2 +magno 2 +locis 2 +quando 2 +cura 2 +locus 2 +tertio 2 +fidem 2 +cetera 2 +secundo 2 +quare 2 +corpore 2 +urbe 2 +eas 2 +iudicio 2 +bonorum 2 +signa 2 +deos 2 +uiri 2 +consul 2 +homo 2 +unus 2 +sententia 2 +legatum 2 +potuit 2 +decem 2 +numero 2 +quicquam 2 +populo 2 +fore 2 +nostri 2 +quisque 2 +melius 2 +dici 2 +namque 2 +gratia 2 +tuo 2 +prope 2 +corporis 2 +uocant 2 +habeat 2 +ceteris 2 +nullum 2 +sese 2 +altera 2 +omnem 2 +puto 2 +cicero 2 +hi 2 +multum 2 +mare 2 +rex 2 +exercitus 2 +aliqua 2 +actio 2 +uir 2 +quattuor 2 +pecunia 2 +male 2 +dicunt 2 +annos 2 +illius 2 +nullo 2 +fuerat 2 +seruus 2 +potestate 2 +respondit 2 +solet 2 +diximus 2 +dicta 2 +iter 2 +hereditatem 2 +alias 2 +decimo 2 +iuris 2 +anno 2 +uirum 2 +nostris 2 +uitam 2 +rationem 2 +hos 2 +dari 2 +iussit 2 +ipsis 2 +paulo 2 +ulla 2 +debere 2 +milites 2 +habuit 2 +ceterum 2 +nondum 1 +romae 1 +procul 1 +ipsius 1 +castris 1 +agere 1 +huc 1 +quidquid 1 +quinque 1 +rursus 1 +alios 1 +tandem 1 +senatu 1 +militum 1 +uino 1 +ora 1 +actionem 1 +consilium 1 +bonis 1 +omnino 1 +legem 1 +longe 1 +generis 1 +praetor 1 +genera 1 +interim 1 +primus 1 +deorum 1 +tuis 1 +publicam 1 +consilio 1 +dicam 1 +uideatur 1 +heredem 1 +donec 1 +maior 1 +sanguine 1 +ore 1 +duobus 1 +sabinum 1 +alius 1 +eis 1 +amplius 1 +exercitum 1 +fructus 1 +temporis 1 +oculos 1 +tuum 1 +urbis 1 +quemadmodum 1 +data 1 +meis 1 +mi 1 +toto 1 +quippe 1 +dixi 1 +testamento 1 +fuisset 1 +meus 1 +oculis 1 +parum 1 +caesaris 1 +corpora 1 +domus 1 +possessionem 1 +etsi 1 +dicimus 1 +hostes 1 +bonum 1 +litteris 1 +imperium 1 +mei 1 +romam 1 +aliquando 1 +fama 1 +nocte 1 +regem 1 +patre 1 +utique 1 +interdum 1 +plures 1 +animus 1 +meam 1 +quaeque 1 +multi 1 +uera 1 +paene 1 +constat 1 +ii 1 +earum 1 +meae 1 +pedes 1 +poterit 1 +consules 1 +liber 1 +ire 1 +mortis 1 +dicendum 1 +terram 1 +quinto 1 +caelo 1 +hominis 1 +tres 1 +populus 1 +cn 1 +extra 1 +fuerunt 1 +mater 1 +ferre 1 +tanto 1 +hostem 1 +diebus 1 +eiusdem 1 +seu 1 +litteras 1 +metu 1 +quotiens 1 +more 1 +morte 1 +sola 1 +uirtute 1 +condicione 1 +fiat 1 +senatum 1 +bella 1 +mari 1 +malo 1 +iste 1 +illos 1 +scire 1 +sententiam 1 +usum 1 +tempora 1 +tui 1 +sumus 1 +animos 1 +lex 1 +tribus 1 +iterum 1 +exercitu 1 +uiro 1 +medio 1 +di 1 +tuae 1 +partibus 1 +malum 1 +terrae 1 +uirtus 1 +iubet 1 +manibus 1 +annis 1 +opera 1 +uxorem 1 +centum 1 +graeci 1 +deus 1 +patres 1 +duas 1 +dicuntur 1 +fide 1 +quarto 1 +magnum 1 +nil 1 +pertinet 1 +has 1 +nomina 1 +tuam 1 +idque 1 +num 1 +sex 1 +solis 1 +quibusdam 1 +domino 1 +naturae 1 +romanis 1 +uideo 1 +semel 1 +alium 1 +placet 1 +regis 1 +quisquam 1 +mala 1 +deum 1 +capite 1 +uires 1 +datur 1 +ultra 1 +suas 1 +aquae 1 +libertatem 1 +uolo 1 +usu 1 +nostrum 1 +dico 1 +alterius 1 +immo 1 +plerumque 1 +unam 1 +longa 1 +populum 1 +aliquis 1 +ira 1 +noua 1 +domi 1 +plura 1 +uia 1 +possum 1 +futurum 1 +leges 1 +iouis 1 +scribit 1 +facto 1 +filia 1 +coepit 1 +plane 1 +ferro 1 +secum 1 +caelum 1 +nostro 1 +utrumque 1 +dominus 1 +solus 1 +erunt 1 +datum 1 +mulier 1 +iulianus 1 +hostis 1 +duos 1 +seruum 1 +illic 1 +legati 1 +unius 1 +protinus 1 +quanto 1 +digestorum 1 +patri 1 +possint 1 +imperio 1 +debeat 1 +bis 1 +scio 1 +aeque 1 +romano 1 +haberet 1 +solent 1 +uelit 1 +uoce 1 +faciunt 1 +mecum 1 +spem 1 +accepit 1 +dolo 1 +quaeritur 1 +oportere 1 +sin 1 +undique 1 +diceret 1 +oppidum 1 +modi 1 +romanus 1 +nescio 1 +oratio 1 +quondam 1 +factus 1 +pretium 1 +hodie 1 +ueluti 1 +plurimum 1 +natus 1 +horum 1 +equidem 1 +gloria 1 +poena 1 +sequitur 1 +interest 1 +sexto 1 +uoluntate 1 +fecerit 1 +fuerint 1 +nullam 1 +periculum 1 +putant 1 +ciuitatis 1 +naturam 1 +equites 1 +ipsam 1 +casus 1 +significat 1 +annum 1 +caeli 1 +pluribus 1 +tecum 1 +spes 1 +fides 1 +similis 1 +domo 1 +pariter 1 +turba 1 +loci 1 +casu 1 +scripsit 1 +gratiam 1 +uelim 1 +uideretur 1 +uiginti 1 +aquam 1 +uellet 1 +uersus 1 +fundum 1 +media 1 +periculo 1 +hominibus 1 +uoluit 1 +poterat 1 +talis 1 +amor 1 +sacra 1 +causis 1 +prouincia 1 +mali 1 +mille 1 +ostendit 1 +uidentur 1 +sis 1 +primis 1 +puer 1 +fata 1 +magni 1 +olim 1 +praestare 1 +septimo 1 +subito 1 +facilius 1 +ipsos 1 +fecisse 1 +italia 1 +filii 1 +salutem 1 +demum 1 +fortasse 1 +qualis 1 +putat 1 +singulis 1 +loca 1 +ciuitate 1 +profecto 1 +aliquo 1 +credo 1 +petit 1 +tulit 1 +auctoritate 1 +boni 1 +partim 1 +scriptum 1 +minime 1 +plebis 1 +pomponius 1 +cuncta 1 +liberos 1 +reliqua 1 +uidit 1 +fortunae 1 +uolunt 1 +ordine 1 +causas 1 +quodam 1 +summo 1 +maxima 1 +arte 1 +multos 1 +causae 1 +suorum 1 +uult 1 +inquam 1 +conuenit 1 +seruo 1 +memoria 1 +saepius 1 +petere 1 +tela 1 +uenire 1 +antea 1 +finem 1 +membra 1 +rege 1 +opere 1 +pectore 1 +frater 1 +nulli 1 +potestatem 1 +etiamsi 1 +malis 1 +miles 1 +ueteres 1 +amore 1 +fiunt 1 +nimis 1 +legiones 1 +temporibus 1 +ecce 1 +placuit 1 +serui 1 +dicat 1 +spe 1 +actione 1 +liberis 1 +hostibus 1 +oratione 1 +potes 1 +praecipue 1 +uiribus 1 +accipere 1 +crimen 1 +iuppiter 1 +uiris 1 +officio 1 +studio 1 +uxor 1 +dat 1 +iuxta 1 +ingens 1 +agros 1 +sensus 1 +paucis 1 +uere 1 +prior 1 +refert 1 +signum 1 +uinum 1 +partis 1 +possent 1 +eandem 1 +habebat 1 +iamque 1 +par 1 +sidera 1 +ignis 1 +uerborum 1 +beneficium 1 +neminem 1 +quarum 1 +uidere 1 +consulem 1 +romanos 1 +eundem 1 +patriae 1 +totam 1 +uerbum 1 +foro 1 +iniuria 1 +maiores 1 +romanum 1 +auctor 1 +moenia 1 +ciuium 1 +familias 1 +testamentum 1 +heredes 1 +aetas 1 +manum 1 +terras 1 +altero 1 +legibus 1 +legis 1 +certum 1 +forma 1 +uitia 1 +totius 1 +dein 1 +summam 1 +noster 1 +profectus 1 +nihilo 1 +antequam 1 +quicquid 1 +uiam 1 +uocatur 1 +iii 1 +utraque 1 +uitium 1 +domini 1 +medium 1 +dolor 1 +filiam 1 +nimium 1 +facies 1 +nostrae 1 +ni 1 +persona 1 +dextra 1 +octauo 1 +posuit 1 +italiam 1 +proelio 1 +aciem 1 +mora 1 +uiros 1 +homini 1 +operis 1 +repente 1 +auro 1 +ultro 1 +titio 1 +etenim 1 +frustra 1 +culpa 1 +liceat 1 +mente 1 +fuga 1 +maiore 1 +possumus 1 +equitum 1 +motus 1 +imperator 1 +operam 1 +summum 1 +uelle 1 +agi 1 +dolore 1 +quaesitum 1 +exemplum 1 +faciat 1 +minor 1 +uale 1 +melle 1 +peruenit 1 +teneri 1 +matrem 1 +solo 1 +caesarem 1 +terris 1 +quanta 1 +sponte 1 +tertia 1 +soli 1 +aceto 1 +species 1 +acie 1 +circum 1 +consulibus 1 +nominis 1 +iura 1 +uidebatur 1 +praesertim 1 +fructum 1 +mors 1 +hercle 1 +isdem 1 +anni 1 +condicio 1 +pecuniae 1 +dicendi 1 +naues 1 +pertinere 1 +uiuere 1 +dolorem 1 +uox 1 +faciam 1 +actum 1 +misit 1 +orbem 1 +metus 1 +palam 1 +urbes 1 +opes 1 +ordinem 1 +quomodo 1 +agro 1 +habeo 1 +aliquem 1 +homine 1 +grauis 1 +matris 1 +nono 1 +pedibus 1 +quot 1 +septem 1 +noui 1 +foret 1 +mori 1 +mores 1 +quaero 1 +ciuitas 1 +militibus 1 +patria 1 +reus 1 +capitis 1 +merito 1 +uisum 1 +aetate 1 +iniuriam 1 +quaestionum 1 +speciem 1 +tuus 1 +simile 1 +uerbo 1 +intus 1 +oleo 1 +quom 1 +ferunt 1 +regnum 1 +numerum 1 +uidi 1 +animis 1 +magistratus 1 +certa 1 +mentem 1 +tertium 1 +aliam 1 +debent 1 +ingenio 1 +aurum 1 +orationis 1 +alibi 1 +nota 1 +pacem 1 +honorem 1 +sole 1 +flumen 1 +communi 1 +uictoria 1 +aetatis 1 +uides 1 +nuper 1 +dis 1 +fratrem 1 +maius 1 +uirtutem 1 +consulto 1 +terrarum 1 +duae 1 +hae 1 +liberum 1 +porro 1 +ter 1 +cumque 1 +deo 1 +fidei 1 +acies 1 +copia 1 +mundi 1 +eaque 1 +maiorem 1 +magnis 1 +alienum 1 +appellant 1 +nascitur 1 +officium 1 +pati 1 +agrum 1 +puta 1 +ideoque 1 +imperii 1 +uestra 1 +duces 1 +prouinciam 1 +uictor 1 +uirtutis 1 +dominum 1 +nauibus 1 +reliquit 1 +uelis 1 +diligenter 1 +liberi 1 +moribus 1 +quadam 1 +cuique 1 +agit 1 +esto 1 +honore 1 +plerique 1 +proinde 1 +possis 1 +agri 1 +cato 1 +cuiusque 1 +legatos 1 +poenam 1 +singulos 1 +consule 1 +nox 1 +apparet 1 +tuos 1 +tenet 1 +iiii 1 +pompeius 1 +tellus 1 +fortunam 1 +praesidio 1 +scipio 1 +postremo 1 +uicensimo 1 +ciuitatem 1 +faceret 1 +interea 1 +kal 1 +tabulas 1 +ordo 1 +pectora 1 +exemplo 1 +prodest 1 +facti 1 +habes 1 +similiter 1 +idcirco 1 +iuuenis 1 +os 1 +maris 1 +pondere 1 +tanti 1 +aliquam 1 +utroque 1 +legatus 1 +longius 1 +magnam 1 +occidit 1 +ullo 1 +arbitror 1 +auxilium 1 +gentis 1 +mirum 1 +roma 1 +optime 1 +titius 1 +nostros 1 +gentes 1 +lingua 1 +marcellus 1 +responsorum 1 +dubium 1 +publico 1 +sequi 1 +pari 1 +tria 1 +uocem 1 +utriusque 1 +ciuis 1 +ceteri 1 +alteri 1 +regna 1 +cursu 1 +fieret 1 +uultus 1 +posita 1 +duabus 1 +pace 1 +patriam 1 +litora 1 +dixisse 1 +loqui 1 +prouinciae 1 +tuas 1 +carmina 1 +isti 1 +dederit 1 +romanorum 1 +copiis 1 +luce 1 +regi 1 +dotem 1 +uisa 1 +initio 1 +intellegi 1 +herede 1 +romana 1 +saxa 1 +scripta 1 +aeris 1 +age 1 +labeo 1 +publice 1 +nullus 1 +proxima 1 +aliae 1 +legata 1 +munus 1 +noctem 1 +pretio 1 +familiae 1 +duce 1 +fertur 1 +isto 1 +proprie 1 +sermone 1 +καὶ 1 +mens 1 +libertas 1 +quemquam 1 +reges 1 +semen 1 +sim 1 +terga 1 +amici 1 +putas 1 +sol 1 +decus 1 +habeant 1 +ingenti 1 +appellatur 1 +gentium 1 +hereditas 1 +uini 1 +uterque 1 +dixerit 1 +negat 1 +quisquis 1 +ferrum 1 +legatis 1 +melior 1 +poenas 1 +spatium 1 +bono 1 +scelus 1 +singula 1 +litterae 1 +negotium 1 +filiae 1 +gaius 1 +mentis 1 +uni 1 +latus 1 +alteram 1 +argentum 1 +dignum 1 +facinus 1 +lumina 1 +materia 1 +nefas 1 +aere 1 +ueneris 1 +uidet 1 +quaeso 1 +parua 1 +uehementer 1 +princeps 1 +agitur 1 +artis 1 +dic 1 +libris 1 +copias 1 +specie 1 +fratres 1 +priusquam 1 +singulari 1 +quosdam 1 +seruos 1 +communis 1 +diutius 1 +fideicommissum 1 +ignem 1 +magnitudine 1 +senex 1 +graecis 1 +nouum 1 +oris 1 +quanti 1 +honores 1 +triginta 1 +uenisse 1 +dei 1 +tale 1 +spiritus 1 +principes 1 +tecta 1 +umbra 1 +certo 1 +istum 1 +orbis 1 +pugna 1 +passus 1 +ualet 1 +dumtaxat 1 +istis 1 +natum 1 +uirgo 1 +fratris 1 +uidemus 1 +insula 1 +italiae 1 +iussu 1 +optimum 1 +hoste 1 +plena 1 +tutor 1 +nonne 1 +alieno 1 +templum 1 +alto 1 +pondo 1 +acta 1 +aliena 1 +ceteros 1 +labore 1 +aiunt 1 +fas 1 +furti 1 +temere 1 +longo 1 +aures 1 +cito 1 +ducere 1 +ualde 1 +beneficio 1 +potestas 1 +aliquod 1 +ciues 1 +nostram 1 +potuisse 1 +reddere 1 +spatio 1 +meos 1 +proelium 1 +utinam 1 +auctores 1 +aequo 1 +finis 1 +illorum 1 +paulum 1 +secunda 1 +legato 1 +peculio 1 +praeda 1 +talia 1 +uoluntatem 1 +forum 1 +praesens 1 +uictus 1 +utile 1 +breui 1 +hispania 1 +relicta 1 +fugit 1 +litore 1 +libertate 1 +proelia 1 +postero 1 +uestigia 1 +caede 1 +ingenium 1 +futura 1 +maioribus 1 +maximum 1 +aliquot 1 +familia 1 +ignes 1 +magnus 1 +tali 1 +simili 1 +curam 1 +dato 1 +digna 1 +munera 1 +tribuni 1 +damnum 1 +pacto 1 +sicilia 1 +uota 1 +agmen 1 +capta 1 +dixerunt 1 +feci 1 +istius 1 +pacis 1 +temporum 1 +diuus 1 +pugnae 1 +restituere 1 +sanguinem 1 +signis 1 +consilia 1 +aeneas 1 +diues 1 +hereditatis 1 +modis 1 +passim 1 +habebit 1 +ubique 1 +proprium 1 +amicis 1 +inuidia 1 +missus 1 +admodum 1 +agmine 1 +dux 1 +folia 1 +istuc 1 +clam 1 +dicendo 1 +gloriam 1 +labor 1 +ducem 1 +uitio 1 +uiuo 1 +condicionem 1 +diuersa 1 +eoque 1 +infelix 1 +maximus 1 +miser 1 +sal 1 +scribere 1 +fugam 1 +maritus 1 +nauis 1 +referre 1 +iudex 1 +africa 1 +ferme 1 +uocat 1 +priore 1 +itinere 1 +libertatis 1 +tenetur 1 +uirorum 1 +extemplo 1 +fundo 1 +pectus 1 +positum 1 +sanguis 1 +felix 1 +impetu 1 +caesare 1 +memoriam 1 +orationem 1 +ultima 1 +artus 1 +pedum 1 +adulescens 1 +bonus 1 +capere 1 +ciuitates 1 +oleum 1 +orbe 1 +penitus 1 +templa 1 +quaecumque 1 +sicuti 1 +mane 1 +necessaria 1 +oppido 1 +uarro 1 +actiones 1 +uideamus 1 +audire 1 +dubio 1 +uenus 1 +colore 1 +fecerunt 1 +ossa 1 +perinde 1 +commune 1 +milibus 1 +alta 1 +cotidie 1 +iussus 1 +praetore 1 +socios 1 +agris 1 +clamore 1 +contentus 1 +equos 1 +heredi 1 +pueri 1 +redit 1 +iudicem 1 +praesidium 1 +utrimque 1 +datus 1 +uenisset 1 +amicos 1 +principem 1 +rogo 1 +sanguinis 1 +ullum 1 +fines 1 +istud 1 +publicum 1 +uulnera 1 +aetatem 1 +modus 1 +sociis 1 +tradunt 1 +egit 1 +nata 1 +opinor 1 +tantam 1 +uultu 1 +uetus 1 +attico 1 +equis 1 +noctis 1 +fortis 1 +fuere 1 +fundi 1 +istam 1 +uisus 1 +ager 1 +amicum 1 +flumina 1 +hereditate 1 +antonius 1 +heredibus 1 +sacris 1 +equo 1 +morbo 1 +lacrimis 1 +potui 1 +praetorem 1 +tergo 1 +incipit 1 +matre 1 +publicis 1 +euenit 1 +crimine 1 +foliis 1 +neue 1 +paulatim 1 +undis 1 +essem 1 +marito 1 +pompei 1 +totidem 1 +utque 1 +asia 1 +patitur 1 +pauca 1 diff --git a/style.less b/style.less index 5484c2f..7211d4c 100644 --- a/style.less +++ b/style.less @@ -1 +1,37 @@ -/* This file is no longer in use */ \ No newline at end of file +body.botmon_captcha { + main { + h1 { + color: transparent !important; + text-shadow: 0 0 .25em rgba(0,.0,0,.8); + } + p, h2, h3, h4, h5, h6 { + color: transparent !important; + text-shadow: 0 0 .35em rgba(0,0,0,.5); + user-select: none; + } + } + #botmon_captcha_box { + border: red dotted 2pt; + position: fixed; + width: 400px; + height: 400px; + top: ~"calc(50vh - 200px)"; + left: ~"calc(50vw - 200px)"; + border-radius: .5rem; + margin: 0; padding: .5rem; + } +} + +/* dark mode overrides */ +@media (prefers-color-scheme: dark) { + body.darkmode.botmon_captcha { + main { + h1 { + text-shadow: 0 0 .25em rgba(170,170,170,.75); + } + p, h2, h3, h4, h5, h6 { + text-shadow: 0 0 .35em rgba(170,170,170,.75); + } + } + } +} \ No newline at end of file From cdc02cd4c39f99df88e41b6048475b40280560ad Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Thu, 23 Oct 2025 20:56:24 +0200 Subject: [PATCH 03/42] Captcha improvements --- action.php | 12 ++-- captcha.js | 54 +++++++++++++---- img/busy-light.svg | 139 +++++++++++++++++++++++++++++++++++++++++++ lang/en/wordlist.txt | 2 - style.less | 104 ++++++++++++++++++++++++++++++-- 5 files changed, 286 insertions(+), 25 deletions(-) create mode 100644 img/busy-light.svg diff --git a/action.php b/action.php index 4a9bc68..d526b92 100644 --- a/action.php +++ b/action.php @@ -208,14 +208,16 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { private function checkCaptchaCookie() { - $cookieVal = isset($_COOKIE['captcha']) ? $_COOKIE['captcha'] : null; + $cookieVal = isset($_COOKIE['DWConfirm']) ? $_COOKIE['DWConfirm'] : null; - $today = new DateTime(); - $isodate = substr((new DateTime())->format('c'), 0, 10); + $today = substr((new DateTime())->format('c'), 0, 10); - $raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $isodate; + $raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today; + $expected = hash('sha256', $raw); - return $cookieVal !== hash('sha256', $raw); + //echo '
  • cookie: ' . $cookieVal . '
  • expected: ' . $expected . '
  • matches: ' .($cookieVal == $expected ? 'true' : 'false') . '
'; + + return $cookieVal !== $expected; } private function insertCaptchaLoader() { diff --git a/captcha.js b/captcha.js index beed194..9f64912 100644 --- a/captcha.js +++ b/captcha.js @@ -1,6 +1,6 @@ "use strict"; /* DokuWiki BotMon Captcha JavaScript */ -/* 22.10.2025 - 0.1.0 - pre-release */ +/* 23.10.2025 - 0.1.2 - pre-release */ /* Author: Sascha Leib */ const $BMCaptcha = { @@ -20,20 +20,25 @@ const $BMCaptcha = { const dlg = document.createElement('dialog'); dlg.setAttribute('closedby', 'none'); dlg.setAttribute('open', 'open'); + dlg.classList.add('checking'); dlg.id = 'botmon_captcha_box'; - dlg.innerHTML = '

Captcha box

Checking if you are a human …

'; + dlg.innerHTML = '

Captcha box

Making sure you are a human:

'; // Checkbox: const lbl = document.createElement('label'); + lbl.innerHTML = 'Click to confirm.Checking …Loading …'; const cb = document.createElement('input'); cb.setAttribute('type', 'checkbox'); + cb.setAttribute('disabled', 'disabled'); cb.addEventListener('click', $BMCaptcha._cbCallback); - lbl.appendChild(cb); - lbl.appendChild(document.createTextNode('I am a human.')); + lbl.prepend(cb); dlg.appendChild(lbl); bm_parent.appendChild(dlg); + + // call the delayed callback in a couple of seconds: + setTimeout($BMCaptcha._delayedCallback, 1500); }, /* creates a digest hash for the cookie function */ @@ -147,19 +152,42 @@ const $BMCaptcha = { if (e.target.checked) { //document.getElementById('botmon_captcha_box').close(); - // make a hash for the cookie: - const seed = document._botmon.seed || ''; - const extIp = document._botmon.ip || '0.0.0.0'; - const d = new Date(document._botmon.t0); - const raw = seed + '|' + location.hostname + '|' + extIp + '|' + d.toISOString().substring(0, 10); + const dat = [ // the data to encode + document._botmon.seed || '', + location.hostname, + document._botmon.ip || '0.0.0.0', + (document._botmon.t0 ? new Date(document._botmon.t0) : new Date()).toISOString().substring(0, 10) + ]; + const hash = $BMCaptcha.digest.hash(dat.join('|')); - const hash = $BMCaptcha.digest.hash(raw); - console.log('Setting cookie to:', raw, ' --> ', hash); - document.cookie = "captcha=" + hash + ';'; + // set the cookie: + document.cookie = "DWConfirm=" + hash + ';path=/;'; + // change the interface: + const dlg = document.getElementById('botmon_captcha_box'); + if (dlg) { + dlg.classList.remove('ready'); + dlg.classList.add('loading'); + } + + // reload the page: window.location.reload(true); } - } + }, + + _delayedCallback: function() { + const dlg = document.getElementById('botmon_captcha_box'); + if (dlg) { + dlg.classList.remove('checking'); + dlg.classList.add('ready'); + + const input = dlg.getElementsByTagName('input')[0]; + if (input) { + input.removeAttribute('disabled'); + input.focus(); + } + } + }, } // initialise the captcha module: diff --git a/img/busy-light.svg b/img/busy-light.svg new file mode 100644 index 0000000..ce57394 --- /dev/null +++ b/img/busy-light.svg @@ -0,0 +1,139 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/lang/en/wordlist.txt b/lang/en/wordlist.txt index 527810d..dfc4bfc 100644 --- a/lang/en/wordlist.txt +++ b/lang/en/wordlist.txt @@ -520,7 +520,6 @@ road 8 late 8 stand 8 suppose 8 -la 8 daughter 8 real 8 nearly 8 @@ -1955,7 +1954,6 @@ ends 2 shop 2 stairs 2 pardon 2 -gay 2 beg 2 seldom 2 kinds 2 diff --git a/style.less b/style.less index 7211d4c..0f6cb90 100644 --- a/style.less +++ b/style.less @@ -11,15 +11,95 @@ body.botmon_captcha { } } #botmon_captcha_box { - border: red dotted 2pt; + & { position: fixed; width: 400px; - height: 400px; - top: ~"calc(50vh - 200px)"; + height: 220px; + top: ~"calc(50vh - 110px)"; left: ~"calc(50vw - 200px)"; - border-radius: .5rem; - margin: 0; padding: .5rem; + background: #1C1B22 none; + border: #7C7B82 solid 1pt; + border-radius: 12px; + margin: 0; padding: 18px; + font-family: system-ui, sans-serif; + box-shadow: .25rem .25rem .5rem rgba(0,0,0,.5); + box-sizing: border-box; } + & * { + color: #EDEDF5; + margin: 0; + } + h2 { + font-size: 24px; + line-height: 32px; + padding: 0 0 12px 0; + } + p { + font-size: 16px; + line-height: 20px; + padding: 6px 0; + } + label { + & { + display: grid; + grid-template-columns: 32px auto; + column-gap: 8px; + align-items: center; + padding: 24px; + margin: 16px 0 0 0; + background-color: #32313A; + border: #7C7B82 solid 1px; + border-radius: 3px; + font-size: 16px; + } + span.busy { + display: inline-block; + width: 24px; height: 24px; + background: transparent url('img/busy-light.svg') center no-repeat; + background-size: 24px; + } + } + input[type="checkbox"] { + width: 24px; height: 24px; + border: 2px solid #00CADB; + border-radius: 4px; + appearance: none; + } + input[type="checkbox"]:focus { + outline: none; + box-shadow: 0 0 6px rgba(0, 202, 219, .8); + } + + input[type="checkbox"]:disabled { + display: none; + } + &.checking { + input[type="checkbox"], span.confirm, span.loading { display: none;} + span.busy, span.checking { display: initial; } + label, input[type="checkbox"] { cursor: none; } + } + &.ready { + input[type="checkbox"], span.confirm { display: initial;} + span.busy, span.checking, span.loading { display: none; } + label, input[type="checkbox"] { cursor: pointer; } + } + &.loading { + span.busy, span.loading { display: initial; } + input[type="checkbox"], span.confirm, span.checking { display: none;} + label { cursor: busy; } + } + } +} + +// smaller screens: +@media (max-width: 480px) { + body.botmon_captcha #botmon_captcha_box { + width: 100vw; + height: auto; + left: 0; top: 80px; + border-radius: 2px; + z-index: 10001; + } } /* dark mode overrides */ @@ -33,5 +113,19 @@ body.botmon_captcha { text-shadow: 0 0 .35em rgba(170,170,170,.75); } } + #botmon_captcha_box { + & { + background-color: #FFF; + border-color: #9F9EA1; + box-shadow: .25rem .25rem .5rem rgba(0,0,0,.25); + } + & * { + color: #15141A; + } + label { + background-color: #EEE; + border-color: #9F9EA1; + } + } } } \ No newline at end of file From 2c641262223e62a95e87ba791a84b69fdaf4310b Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Fri, 24 Oct 2025 10:02:56 +0200 Subject: [PATCH 04/42] Whitelisting enabled --- action.php | 48 ++++- config/default-whitelist.txt | 340 +++++++++++++++++++++++++++++++++++ style.less | 4 +- 3 files changed, 387 insertions(+), 5 deletions(-) create mode 100644 config/default-whitelist.txt diff --git a/action.php b/action.php index d526b92..75869d0 100644 --- a/action.php +++ b/action.php @@ -43,6 +43,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { /* session information */ private $sessionId = null; private $sessionType = ''; + private $showCaptcha = '-'; /** * Inserts tracking code to the page header @@ -118,8 +119,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $_SERVER['HTTP_REFERER'] ?? '', /* HTTP Referrer */ substr($conf['lang'],0,2), /* page language */ implode(',', array_unique(array_map( function($it) { return substr(trim($it),0,2); }, explode(',',trim($_SERVER['HTTP_ACCEPT_LANGUAGE'], " \t;,*"))))), /* accepted client languages */ - $this->getCountryCode() /* GeoIP country code */ - ); + $this->getCountryCode(), /* GeoIP country code */ + $this->showCaptcha /* show captcha? */ ); //* create the log line */ $filename = __DIR__ .'/logs/' . gmdate('Y-m-d') . '.srv.txt'; /* use GMT date for filename */ @@ -191,7 +192,10 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $useCaptcha = $this->getConf('useCaptcha'); - if ($useCaptcha !== 'disabled' && $this->checkCaptchaCookie()) { + if ($useCaptcha !== 'disabled' && $this->checkCaptchaCookie() && !$this->captchaWhitelisted()) { + + $this->showCaptcha = 'Y'; // captcha will be shown. + echo '

'; tpl_pagetitle(); echo "

\n"; // always show the original page title $event->preventDefault(); // don't show normal content switch ($useCaptcha) { @@ -203,6 +207,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { break; } $this->insertCaptchaLoader(); // and load the captcha + } else { + $this->showCaptcha = 'N'; // do not show a captcha } } @@ -220,6 +226,42 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { return $cookieVal !== $expected; } + // check if the visitor's IP is on a whitelist: + private function captchaWhitelisted() { + + // normalise IP address: + $ip = inet_pton($_SERVER['REMOTE_ADDR']); + + // find which file to open: + $prefixes = ['user', 'default']; + foreach ($prefixes as $pre) { + $filename = __DIR__ .'/config/' . $pre . '-whitelist.txt'; + if (file_exists($filename)) { + break; + } + } + + if (file_exists($filename)) { + $lines = file($filename, FILE_SKIP_EMPTY_LINES); + foreach ($lines as $line) { + if (trim($line) !== '' && !str_starts_with($line, '#')) { + $col = explode("\t", $line); + if (count($col) >= 2) { + $from = inet_pton($col[0]); + $to = inet_pton($col[1]); + + if ($ip >= $from && $ip <= $to) { + //echo "

Found my IP in range: " . $col[0] . " - " . $col[1] . "

"; + return true; + } + } + } + } + } + + return false; + } + private function insertCaptchaLoader() { echo ''; + + $cCode = '-'; + if ($useCaptcha !== 'disabled') { + if ($this->captchaWhitelisted()) { + $cCode = 'W'; // whitelisted + } + elseif ($this->hasCaptchaCookie()) { + $cCode = 'N'; // user already has a cookie + } + else { + $cCode = 'Y'; // show the captcha + + echo ''; // placeholder image + $event->preventDefault(); // don't show normal content + + // TODO Insert dummy image + $this->insertCaptchaLoader(); // and load the captcha + } + }; + + $this->showCaptcha = $cCode; // store the captcha code for the logfile + } + + private function hasCaptchaCookie() { $cookieVal = isset($_COOKIE['DWConfirm']) ? $_COOKIE['DWConfirm'] : null; @@ -223,7 +271,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { //echo '
  • cookie: ' . $cookieVal . '
  • expected: ' . $expected . '
  • matches: ' .($cookieVal == $expected ? 'true' : 'false') . '
'; - return $cookieVal !== $expected; + return $cookieVal == $expected; } // check if the visitor's IP is on a whitelist: @@ -251,15 +299,13 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $to = inet_pton($col[1]); if ($ip >= $from && $ip <= $to) { - //echo "

Found my IP in range: " . $col[0] . " - " . $col[1] . "

"; - return true; + return true; /* IP whitelisted */ } } } } } - - return false; + return false; /* IP not found in whitelist */ } private function insertCaptchaLoader() { diff --git a/admin.css b/admin.css index 734ee97..d8dad0e 100644 --- a/admin.css +++ b/admin.css @@ -108,6 +108,12 @@ &.cl_operaold::before { background-position-y: -380px } &.cl_other::before { background-image: url('img/more.svg') } + /* Captcha statuses */ + &.captcha::before { background-image: url('img/captcha.png') } + &.cap_Y::before { background-position-y: -20px } + &.cap_YN::before { background-position-y: -40px } + &.cap_W::before { background-position-y: -60px } + /* Country flags */ /* Note: flag images and CSS adapted from: https://github.com/lafeber/world-flags-sprite/ */ &.country::before { @@ -730,21 +736,28 @@ background-image: url('img/info.svg') } - /* pageviews */ - span.pageviews { + /* pages seen */ + span.pageseen, span.pageviews { border: #999 solid 1px; padding: 0 2px; font-size: smaller; border-radius: .5em; margin-right: .25em; } - span.pageviews::before { + span.pageseen::before { content : ''; display: inline-block; width: 1.25em; height: 1.25em; background: transparent url('img/page.svg') center no-repeat; background-size: 1.25em; } + span.pageviews::before { + content : ''; + display: inline-block; + width: 1.25em; height: 1.25em; + background: transparent url('img/views.svg') center no-repeat; + background-size: 1.25em; + } } /* item footer */ @@ -890,7 +903,7 @@ border-top-color: #CCC; } } - span.pageviews { + span.pageseen, span.pageviews { border-color: #555; } diff --git a/admin.js b/admin.js index 49765e5..0661f9e 100644 --- a/admin.js +++ b/admin.js @@ -291,7 +291,7 @@ BotMon.live = { // shortcut to make code more readable: const model = BotMon.live.data.model; - const timeout = 60 * 60 * 1000; // session timeout: One hour + //const timeout = 60 * 60 * 1000; // session timeout: One hour if (visitor._type == BM_USERTYPE.KNOWN_BOT) { // known bots match by their bot ID: @@ -303,13 +303,14 @@ BotMon.live = { return v; } } + } else { // other types match by their DW/PHPIDs: // loop over all visitors already registered and check for ID matches: for (let i=0; i nv.ts) { - visitor._firstSeen = nv.ts; - } + }; + + // update first and last seen: + if (visitor._firstSeen > nv.ts) { + visitor._firstSeen = nv.ts; + } + if (visitor._lastSeen < nv.ts) { + visitor._lastSeen = nv.ts; } - // find browser + // update total loads and views (not the same!): + visitor._loadCount += 1; + visitor._viewCount += (nv.captcha == 'Y' ? 0 : 1); + + // ...because also a captcha is a "load", but not a "view". + // let's count the captcha statuses as well: + if (nv.captcha) visitor._captcha[nv.captcha] += 1; // is this visit already registered? let prereg = model._getPageView(visitor, nv); @@ -401,14 +416,15 @@ BotMon.live = { // add new page view: prereg = model._makePageView(nv, type); visitor._pageViews.push(prereg); - } else { - // update last seen date - prereg._lastSeen = nv.ts; - // increase view count: - prereg._viewCount += 1; - prereg._tickCount += 1; } + // update last seen date + prereg._lastSeen = nv.ts; + + // increase view count: + prereg._loadCount += (visitor.captcha == 'Y' ? 0 : 1); + //prereg._tickCount += 1; + // update referrer state: visitor._hasReferrer = visitor._hasReferrer || (prereg.ref !== undefined && prereg.ref !== ''); @@ -520,7 +536,8 @@ BotMon.live = { _lastSeen: data.ts, _seenBy: [type], _jsClient: ( type !== BM_LOGTYPE.SERVER), - _viewCount: 1, + _viewCount: 0, + _loadCount: 0, _tickCount: 0 }; } @@ -572,19 +589,19 @@ BotMon.live = { // count visits and page views: this.data.totalVisits += 1; - this.data.totalPageViews += v._pageViews.length; + this.data.totalPageViews += v._viewCount; // check for typical bot aspects: let botScore = 0; if (v._type == BM_USERTYPE.KNOWN_BOT) { // known bots - this.data.bots.known += v._pageViews.length; + this.data.bots.known += v._viewCount; this.groups.knownBots.push(v); } else if (v._type == BM_USERTYPE.KNOWN_USER) { // known users */ - this.data.bots.users += v._pageViews.length; + this.data.bots.users += v._viewCount; this.groups.users.push(v); } else { @@ -596,11 +613,11 @@ BotMon.live = { if (e.isBot) { // likely bots v._type = BM_USERTYPE.LIKELY_BOT; - this.data.bots.suspected += v._pageViews.length; + this.data.bots.suspected += v._viewCount; this.groups.suspectedBots.push(v); } else { // probably humans v._type = BM_USERTYPE.PROBABLY_HUMAN; - this.data.bots.human += v._pageViews.length; + this.data.bots.human += v._viewCount; this.groups.humans.push(v); } } @@ -640,7 +657,7 @@ BotMon.live = { //console.log(BotMon.live.data.analytics.groups.knownBots); let botsList = BotMon.live.data.analytics.groups.knownBots.toSorted( (a, b) => { - return b._pageViews.length - a._pageViews.length; + return b._viewCount - a._viewCount; }); const other = { @@ -659,12 +676,12 @@ BotMon.live = { rList.push({ id: it._bot.id, name: (it._bot.n ? it._bot.n : it._bot.id), - count: it._pageViews.length + count: it._viewCount }); } else { other.count += it._pageViews.length; }; - total += it._pageViews.length; + total += it._viewCount; } }; @@ -997,7 +1014,7 @@ BotMon.live = { const list = me.groups[type]; list.forEach(it => { - bounces += (it._pageViews.length <= 1 ? 1 : 0); + bounces += (it._viewCount <= 1 ? 1 : 0); }); return bounces; @@ -1085,7 +1102,7 @@ BotMon.live = { } // add to counter: - ipRec.count += v._pageViews.length; + ipRec.count += v._viewCount; }, @@ -1520,12 +1537,13 @@ BotMon.live = { // are there at lest num pages loaded? smallPageCount: function(visitor, num) { - return (visitor._pageViews.length <= Number(num)); + return (visitor._viewCount <= Number(num)); }, // There was no entry in a specific log file for this visitor: // note that this will also trigger the "noJavaScript" rule: noRecord: function(visitor, type) { + if (!visitor._seenBy.includes('srv')) return false; // only if 'srv' is also specified! return !visitor._seenBy.includes(type); }, @@ -1613,7 +1631,7 @@ BotMon.live = { // At least x page views were recorded, but they come within less than y seconds loadSpeed: function(visitor, minItems, maxTime) { - if (visitor._pageViews.length >= minItems) { + if (visitor._viewCount >= minItems) { //console.log('loadSpeed', visitor._pageViews.length, minItems, maxTime); const pvArr = visitor._pageViews.map(pv => pv._lastSeen).sort(); @@ -1704,7 +1722,7 @@ BotMon.live = { switch (type) { case "srv": typeName = "Server"; - columns = ['ts','ip','pg','id','typ','usr','agent','ref','lang','accept','geo']; + columns = ['ts','ip','pg','id','typ','usr','agent','ref','lang','accept','geo','captcha']; break; case "log": typeName = "Page load"; @@ -2299,20 +2317,11 @@ BotMon.live = { }, data.id)); } - // seen by icon: - span1.appendChild(make('span', { - 'class': 'icon_only seenby sb_' + data._seenBy.join(''), - 'title': "Seen by: " + data._seenBy.join('+') - }, data._seenBy.join(', '))); + span1.appendChild(make('span', { /* page views */ + 'class': 'has_icon pageseen', + 'title': data._pageViews.length + " page load(s)" + }, data._pageViews.length)); - // country flag: - if (data.geo && data.geo !== 'ZZ') { - span1.appendChild(make('span', { - 'class': 'icon_only country ctry_' + data.geo.toLowerCase(), - 'data-ctry': data.geo, - 'title': "Country: " + ( data._country || "Unknown") - }, ( data._country || "Unknown") )); - } // referer icons: if ((data._type == BM_USERTYPE.PROBABLY_HUMAN || data._type == BM_USERTYPE.LIKELY_BOT) && data.ref) { @@ -2326,15 +2335,26 @@ BotMon.live = { summary.appendChild(span1); const span2 = make('span'); /* right-hand group */ - span2.appendChild(make('span', { /* first-seen */ - 'class': 'has_iconfirst-seen', - 'title': "First seen: " + data._firstSeen.toLocaleString() + " UTC" - }, BotMon.t._formatTime(data._firstSeen))); + // country flag: + if (data.geo && data.geo !== 'ZZ') { + span2.appendChild(make('span', { + 'class': 'icon_only country ctry_' + data.geo.toLowerCase(), + 'data-ctry': data.geo, + 'title': "Country: " + ( data._country || "Unknown") + }, ( data._country || "Unknown") )); + } - span2.appendChild(make('span', { /* page views */ - 'class': 'has_icon pageviews', - 'title': data._pageViews.length + " page view(s)" - }, data._pageViews.length)); + span2.appendChild(make('span', { // seen-by icon: + 'class': 'icon_only seenby sb_' + data._seenBy.join(''), + 'title': "Seen by: " + data._seenBy.join('+') + }, data._seenBy.join(', '))); + + const captchaCode = '' + ( data._captcha['Y'] > 0 ? 'Y' : '' ) + ( data._captcha['N'] > 0 ? 'N' : '' ) + ( data._captcha['W'] > 0 ? 'W' : '' ); + if (captchaCode !== '') { + span2.appendChild(make('span', { // captcha status + 'class': 'icon_only captcha cap_' + captchaCode, + }, captchaCode)); + } summary.appendChild(span2); @@ -2414,6 +2434,13 @@ BotMon.live = { dl.appendChild(make('dd', {'class': 'lastSeen'}, data._lastSeen.toLocaleString())); } + dl.appendChild(make('dt', {}, "Actions:")); + dl.appendChild(make('dd', {'class': 'views'}, + "Page loads: " + data._loadCount.toString() + + ( data._captcha['Y'] > 0 || data._captcha['W'] || data._captcha['-'] > 0 ? ", captchas: " + (data._captcha['Y']+data._captcha['W']+data._captcha['-']).toString() : '') + + ", views: " + data._viewCount.toString() + )); + dl.appendChild(make('dt', {}, "User-Agent:")); dl.appendChild(make('dd', {'class': 'agent'}, data.agent)); @@ -2441,6 +2468,12 @@ BotMon.live = { 'title': "Country: " + data._country }, data._country + ' (' + data.geo + ')')); } + if (data.captcha && data.captcha !=='') { + dl.appendChild(make('dt', {}, "Captcha-status:")); + dl.appendChild(make('dd', { + 'class': 'captcha' + }, data.captcha)); + } dl.appendChild(make('dt', {}, "Session ID:")); dl.appendChild(make('dd', {'class': 'has_icon session typ_' + data.typ}, data.id)); @@ -2528,12 +2561,19 @@ BotMon.live = { 'title': "PageID: " + page.pg }, page.pg)); /* DW Page ID */ - // get the time difference: - row1.appendChild(make('span', { - 'class': 'first-seen', - 'title': "First visited: " + page._firstSeen.toLocaleString() + " UTC" - }, BotMon.t._formatTime(page._firstSeen))); - + const rightGroup = row1.appendChild(make('div')); // right-hand group + + // get the time difference: + rightGroup.appendChild(make('span', { + 'class': 'first-seen', + 'title': "First visited: " + page._firstSeen.toLocaleString() + " UTC" + }, BotMon.t._formatTime(page._firstSeen))); + + rightGroup.appendChild(make('span', { /* page loads */ + 'class': 'has_icon pageviews', + 'title': page._viewCount.toString() + " page load(s)" + }, page._viewCount.toString())); + pgLi.appendChild(row1); /* LINE 2 */ diff --git a/captcha.js b/captcha.js index 9f64912..cb79a4e 100644 --- a/captcha.js +++ b/captcha.js @@ -26,7 +26,7 @@ const $BMCaptcha = { // Checkbox: const lbl = document.createElement('label'); - lbl.innerHTML = 'Click to confirm.Checking …Loading …'; + lbl.innerHTML = 'Click to confirm.Checking …Loading …An error occured.'; const cb = document.createElement('input'); cb.setAttribute('type', 'checkbox'); cb.setAttribute('disabled', 'disabled'); @@ -152,26 +152,35 @@ const $BMCaptcha = { if (e.target.checked) { //document.getElementById('botmon_captcha_box').close(); - const dat = [ // the data to encode - document._botmon.seed || '', - location.hostname, - document._botmon.ip || '0.0.0.0', - (document._botmon.t0 ? new Date(document._botmon.t0) : new Date()).toISOString().substring(0, 10) - ]; - const hash = $BMCaptcha.digest.hash(dat.join('|')); + try { + var $status = 'loading'; - // set the cookie: - document.cookie = "DWConfirm=" + hash + ';path=/;'; + // generate the hash: + const dat = [ // the data to encode + document._botmon.seed || '', + location.hostname, + document._botmon.ip || '0.0.0.0', + (new Date()).toISOString().substring(0, 10) + ]; + const hash = $BMCaptcha.digest.hash(dat.join('|')); + + // set the cookie: + document.cookie = "DWConfirm=" + hash + ';path=/;'; + + } catch (err) { + console.error(err); + $status = 'error'; + } // change the interface: const dlg = document.getElementById('botmon_captcha_box'); if (dlg) { dlg.classList.remove('ready'); - dlg.classList.add('loading'); + dlg.classList.add( $status ); } // reload the page: - window.location.reload(true); + if ($status !== 'error')window.location.reload(true); } }, diff --git a/conf/default.php b/conf/default.php index 48f2cf5..53540b4 100644 --- a/conf/default.php +++ b/conf/default.php @@ -6,5 +6,5 @@ */ $conf['geoiplib'] = 'disabled'; -$conf['useCaptcha'] = 0; -$conf['captchaSeed'] = 'b472719ba5634d378a7d7f9bfc46659f'; +$conf['useCaptcha'] = 'disabled'; +$conf['captchaSeed'] = 'c53bc5f94929451987efa6c768d8856b'; diff --git a/config/default-whitelist.txt b/config/default-whitelist.txt index eaa0688..397720d 100644 --- a/config/default-whitelist.txt +++ b/config/default-whitelist.txt @@ -335,6 +335,12 @@ 2001:4860:4801:000c:: 2001:4860:4801:000c:FFFF:FFFF:FFFF:FFFF 64 2001:4860:4801:000f:: 2001:4860:4801:000f:FFFF:FFFF:FFFF:FFFF 64 -# PlagAware Bot - from previous visits’ IPs -157.90.90.163 157.90.90.163 32 -2a01:4f8:2190:21a0::2 2a01:4f8:2190:21a0::2 64 +# SeznamBot - IPs from: https://o-seznam.cz/napoveda/vyhledavani/en/seznambot-crawler/ +77.75.76.0 77.75.79.255 22 +2a02:0598:0064:8a00:0000:0000:3100:0000 2a02:0598:0064:8a00:0000:0000:3100:001f 123 +2a02:0598:0128:8a00:0000:0000:0b00:0000 2a02:0598:0128:8a00:0000:0000:0b00:001f 123 +2a02:0598:0096:8a00:0000:0000:1200:0120 2a02:0598:0096:8a00:0000:0000:1200:013f 123 + +# localhosts +127.0.0.1 127.255.255.255 8 +::1 ::1 128 \ No newline at end of file diff --git a/config/known-ipranges.json b/config/known-ipranges.json index 466dddd..4e955cb 100644 --- a/config/known-ipranges.json +++ b/config/known-ipranges.json @@ -1,10 +1,12 @@ { "groups": [ {"id": "alibaba", "name": "Alibaba"}, - {"id": "amazon", "name": "Amazon DS"}, + {"id": "amazon", "name": "Amazon"}, + {"id": "bezeq", "name": "Bezeq Int."}, {"id": "brasilnet", "name": "BrasilNet"}, {"id": "charter", "name": "Charter Inc."}, {"id": "chinanet", "name": "Chinanet"}, + {"id": "cloudflare", "name": "Cloudflare Inc."}, {"id": "cnisp", "name": "China ISP"}, {"id": "cnmob", "name": "China Mobile"}, {"id": "google", "name": "Google LLC"}, @@ -44,7 +46,7 @@ {"from": "52.96.0.0", "to": "52.127.255.254", "m": 11, "g": "microsoft"}, {"from": "54.0.0.0", "to": "54.255.255.254", "m": 8, "g": "amazon"}, {"from": "66.249.64.0", "to": "66.249.95.254", "m": 19, "g": "google"}, - {"from": "84.37.35.0", "to": "84.37.255.254", "g": "gtt"}, + {"from": "82.80.0.0", "to": "82.81.255.255", "m": 15, "g": "bezeq"}, {"from": "94.74.64.0", "to": "94.74.127.254", "m": 18, "g": "huawei"}, {"from": "91.84.96.0", "to": "91.84.127.254", "m": 19, "g": "vdsina"}, {"from": "101.0.0.0", "to": "101.255.255.254", "m": 8,"g": "chinanet"}, @@ -98,6 +100,7 @@ {"from": "2603:8000::::::", "to": "2603:80ff:ffff:ffff:ffff:ffff:ffff:ffff", "m": 24, "g": "charter"}, {"from": "2607:a400::::::", "to": "2607:a400:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "g": "zenlayer"}, {"from": "2804:::::::", "to": "2804:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "m": 16, "g": "misc_sa"}, + {"from": "2a09:bac3::::::", "to": "2a09:bac3:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32,"g": "cloudflare"}, {"from": "2a0a:4cc0::::::", "to": "2a0a:4cc0:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "g": "netcup"} ] } \ No newline at end of file diff --git a/img/captcha.png b/img/captcha.png new file mode 100644 index 0000000000000000000000000000000000000000..6e43e46b9398dcd2ce6643325e4fcffc6b4cd08f GIT binary patch literal 2705 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 b%Kil1QB_LDXVw4u(g92iEf7`u?)Ux+Dn!D~ literal 0 HcmV?d00001 diff --git a/img/stages.png b/img/stages.png index 0ac0c9e63f0e2fb3795cf0bd10fff93b9f0df13c..4d2621f7a7008c399283d728209c79182e7ab3ad 100644 GIT binary patch delta 3938 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 g%iii7L=XFmGw(FsKK2eO=-5$Rg4 Date: Sat, 25 Oct 2025 21:14:35 +0200 Subject: [PATCH 06/42] Localisation --- action.php | 12 ++++++++++++ admin.js | 8 ++++---- admin.php | 8 +++++++- captcha.js | 14 ++++++++++++-- conf/default.php | 1 + conf/metadata.php | 3 +++ config/default-whitelist.txt | 4 ++-- lang/de/lang.php | 14 ++++++++++++++ lang/en/lang.php | 14 ++++++++++++++ lang/en/settings.php | 6 +++++- 10 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 lang/de/lang.php create mode 100644 lang/en/lang.php diff --git a/action.php b/action.php index 46bdcf3..be451e2 100644 --- a/action.php +++ b/action.php @@ -309,6 +309,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { } private function insertCaptchaLoader() { + echo '' . NL; } diff --git a/admin.js b/admin.js index 0661f9e..a684ebc 100644 --- a/admin.js +++ b/admin.js @@ -42,7 +42,7 @@ const BotMon = { // get yesterday's date: let d = new Date(); - d.setDate(d.getDate() - 1); + if (BMSettings.showday == 'yesterday') d.setDate(d.getDate() - 1); this._datestr = d.toISOString().slice(0, 10); // init the sub-objects: @@ -185,7 +185,7 @@ const BotMon = { /* everything specific to the "Latest" tab is self-contained in the "live" object: */ BotMon.live = { init: function() { - console.info('BotMon.live.init()'); + //console.info('BotMon.live.init()'); // set the title: const tDiff = 'UTC ' + (BotMon._timeDiff != '' ? ` (offset: ${BotMon._timeDiff}` : '' ) + ')'; @@ -1056,13 +1056,13 @@ BotMon.live = { const prefix = v.ip.split(':').slice(0, kIP6Segments).join(':'); rawIP = ipSeg.slice(0, kIP6Segments).join(':'); ipGroup = 'ip6-' + rawIP.replaceAll(':', '-'); - ipName = prefix + '::' + '/' + (16 * kIP6Segments); + ipName = prefix + '::'; // + '/' + (16 * kIP6Segments); } else { ipSeg = ipAddr.split('.'); const prefix = v.ip.split('.').slice(0, kIP4Segments).join('.'); rawIP = ipSeg.slice(0, kIP4Segments).join('.') ; ipGroup = 'ip4-' + rawIP.replaceAll('.', '-'); - ipName = prefix + '.x.x.x'.substring(0, 1+(4-kIP4Segments)*2) + '/' + (8 * kIP4Segments); + ipName = prefix + '.x.x.x'.substring(0, 1+(4-kIP4Segments)*2); // + '/' + (8 * kIP4Segments); } } diff --git a/admin.php b/admin.php index a9ca24b..2f30ef3 100644 --- a/admin.php +++ b/admin.php @@ -104,7 +104,13 @@ class admin_plugin_botmon extends AdminPlugin { echo '
  • No files to process.
  • '; } - echo ''; + echo '' . NL; + echo ''; + echo ''; } diff --git a/captcha.js b/captcha.js index cb79a4e..fdbcbc8 100644 --- a/captcha.js +++ b/captcha.js @@ -13,6 +13,13 @@ const $BMCaptcha = { }, install: function() { + + // localisation helper function: + let _loc = function(id, alt) { + if ($BMLocales && $BMLocales[id]) return $BMLocales[id]; + return alt; + } + // find the parent element: let bm_parent = document.getElementsByTagName('body')[0]; @@ -22,11 +29,14 @@ const $BMCaptcha = { dlg.setAttribute('open', 'open'); dlg.classList.add('checking'); dlg.id = 'botmon_captcha_box'; - dlg.innerHTML = '

    Captcha box

    Making sure you are a human:

    '; + dlg.innerHTML = '

    ' + _loc('dlgTitle', 'Title') + '

    ' + _loc('dlgSubtitle', 'Subtitle') + '

    '; // Checkbox: const lbl = document.createElement('label'); - lbl.innerHTML = 'Click to confirm.Checking …Loading …An error occured.'; + lbl.innerHTML = '' + _loc('dlgConfirm', "Confirm.") + '' + + '' + _loc('dlgChecking', "Checking") + '' + + '' + _loc('dlgLoading', "Loading") + '' + + '' + _loc('dlgError', "Error") + ''; const cb = document.createElement('input'); cb.setAttribute('type', 'checkbox'); cb.setAttribute('disabled', 'disabled'); diff --git a/conf/default.php b/conf/default.php index 53540b4..45cf90c 100644 --- a/conf/default.php +++ b/conf/default.php @@ -5,6 +5,7 @@ * @author Sascha Leib */ +$conf['showday'] = 'yesterday'; $conf['geoiplib'] = 'disabled'; $conf['useCaptcha'] = 'disabled'; $conf['captchaSeed'] = 'c53bc5f94929451987efa6c768d8856b'; diff --git a/conf/metadata.php b/conf/metadata.php index 32c61a1..32dd6d0 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -5,6 +5,9 @@ * @author Sascha Leib */ +$meta['showday'] = array('multichoice', + '_choices' => array ('yesterday', 'today')); + $meta['geoiplib'] = array('multichoice', '_choices' => array ('disabled', 'phpgeoip')); diff --git a/config/default-whitelist.txt b/config/default-whitelist.txt index 397720d..093087f 100644 --- a/config/default-whitelist.txt +++ b/config/default-whitelist.txt @@ -342,5 +342,5 @@ 2a02:0598:0096:8a00:0000:0000:1200:0120 2a02:0598:0096:8a00:0000:0000:1200:013f 123 # localhosts -127.0.0.1 127.255.255.255 8 -::1 ::1 128 \ No newline at end of file +#127.0.0.1 127.255.255.255 8 +#::1 ::1 128 \ No newline at end of file diff --git a/lang/de/lang.php b/lang/de/lang.php new file mode 100644 index 0000000..0b240a6 --- /dev/null +++ b/lang/de/lang.php @@ -0,0 +1,14 @@ + + */ + +// Captcha dialog locale strings: +$lang['bm_dlgTitle'] = 'Benutzerüberprüfung'; +$lang['bm_dlgSubtitle'] = 'Bitte bestätige, dass du kein Bot bist:'; +$lang['bm_dlgConfirm'] = 'Klicke, um zu bestätigen.'; +$lang['bm_dlgChecking'] = 'Wird überprüft …'; +$lang['bm_dlgLoading'] = 'Seite wird geladen …'; +$lang['bm_dlgError'] = 'Es ist ein Fehler aufgetreten.'; diff --git a/lang/en/lang.php b/lang/en/lang.php new file mode 100644 index 0000000..b5b9334 --- /dev/null +++ b/lang/en/lang.php @@ -0,0 +1,14 @@ + + */ + +// Captcha dialog locale strings: +$lang['bm_dlgTitle'] = 'User Check'; +$lang['bm_dlgSubtitle'] = 'Please confirm you are not a bot:'; +$lang['bm_dlgConfirm'] = 'Click to confirm.'; +$lang['bm_dlgChecking'] = 'Checking …'; +$lang['bm_dlgLoading'] = 'Loading page …'; +$lang['bm_dlgError'] = 'An error occured.'; diff --git a/lang/en/settings.php b/lang/en/settings.php index 022458c..d42003b 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -5,9 +5,13 @@ * @author Sascha Leib */ +$lang['showday'] = 'Which data to show in the “Latest” tab:'; + $lang['showday_o_yesterday'] = 'Last full day (yesterday)'; + $lang['showday_o_today'] = 'Ongoing logs (today)'; + $lang['geoiplib'] = 'Add GeoIP Information
    (requires PHP module to be installed)'; $lang['geoiplib_o_disabled'] = 'Disabled'; $lang['geoiplib_o_phpgeoip'] = 'Use GeoIP Module'; $lang['useCaptcha'] = 'Enable Captcha
    (Experimental, read the manual first!)'; -$lang['captchaSeed'] = 'Captcha Seed
    (Enter a 16 to 32 digits random number)'; +$lang['captchaSeed'] = 'Captcha Seed
    (Best use a random number, e.g. here )'; From 447b8b4fbe99d4a8eee9e007372c2b4dde8fc42f Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sun, 26 Oct 2025 09:14:45 +0100 Subject: [PATCH 07/42] SessionId fix --- action.php | 15 +++++++++------ admin.css | 1 + config/known-ipranges.json | 2 ++ img/captcha.png | Bin 2705 -> 2740 bytes img/idtyp.png | Bin 3855 -> 4186 bytes 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/action.php b/action.php index be451e2..a14e347 100644 --- a/action.php +++ b/action.php @@ -23,9 +23,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { global $ACT; - // initialize the session id and type with random data: - $this->sessionId = rand(1000000, 9999999); - $this->sessionType = 'rnd'; + // populate the session id and type: + $this->setSessionInfo(); // insert header data into the page: if ($ACT == 'show' || $ACT == 'edit' || $ACT == 'media') { @@ -62,8 +61,6 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { global $INFO; - // populate the session id and type: - $this->getSessionInfo(); // build the tracker code: $code = $this->getBMHeader(); @@ -178,7 +175,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { return $country; } - private function getSessionInfo() { + private function setSessionInfo() { // what is the session identifier? if (isset($_SESSION)) { @@ -199,6 +196,12 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $this->sessionId = $_SERVER['REMOTE_ADDR']; $this->sessionType = 'ip'; } + + if (!$this->sessionId) { /* if all fails, use random data */ + $this->sessionId = rand(100000000, 999999999); + $this->sessionType = 'rnd'; + } + } public function showCaptcha(Event $event) { diff --git a/admin.css b/admin.css index d8dad0e..8b15ce9 100644 --- a/admin.css +++ b/admin.css @@ -363,6 +363,7 @@ &.typ_php::before { background-position-y: -40px } &.typ_ip::before { background-position-y: -60px } &.typ_usr::before { background-position-y: -80px } + &.typ_rnd::before { background-position-y: -100px } /* External link icons */ &.extlink::before { background-image: url('img/links.png') } diff --git a/config/known-ipranges.json b/config/known-ipranges.json index 4e955cb..68dcf4f 100644 --- a/config/known-ipranges.json +++ b/config/known-ipranges.json @@ -20,6 +20,7 @@ {"id": "zenlayer", "name": "Zenlayer"} ], "ranges": [ + {"from": "1.92.0.0", "to": "1.95.255.254", "m": 14, "g": "huawei"}, {"from": "3.0.0.0", "to": "3.255.255.254", "m": 8, "g": "amazon"}, {"from": "5.161.0.0", "to": "5.161.255.255", "m": 16, "g": "hetzner"}, {"from": "8.128.0.0", "to": "8.191.255.254", "m": 10, "g": "alibaba"}, @@ -67,6 +68,7 @@ {"from": "138.121.0.0", "to": "138.121.225.254", "m": 16, "g": "misc_sa"}, {"from": "142.147.128.0", "to": "1142.147.255.254", "m": 17, "g": "w2obj"}, {"from": "146.174.128.0", "to": "146.174.191.254", "m": 18, "g": "huawei"}, + {"from": "149.232.128.0", "to": "149.232.159.254", "m": 19, "g": "huawei"}, {"from": "150.40.128.0", "to": "150.40.255.254", "m": 17, "g": "huawei"}, {"from": "159.138.0.0", "to": "159.138.225.254", "m": 16, "g": "huawei"}, {"from": "162.128.0.0", "to": "162.128.255.254", "m": 16, "g": "zenlayer"}, diff --git a/img/captcha.png b/img/captcha.png index 6e43e46b9398dcd2ce6643325e4fcffc6b4cd08f..f101af1f226a35bd40e4cd2453def0b4f18378b4 100644 GIT binary patch delta 2495 zcmV;w2|)Id6|@zQNeUtW02U$v8y13(kyBOy0+Fg83q>*l0O$h%07Wtak!Z~X z(SomAYprF*i&GtkapDa2X;F)`V70bdsM9(_4Tz4hr2^YmgyL<25kpIk|Z};4DzW<(ovuD5aofFRUQewf~%SvQECVoeJt9YOI#OkGsdt~#X zsDK=gl9?~!xSFyM-Vm3Izm(7+Qxbs@kdsSfmWoSM@surln@mRhx;U0Hp-=q1ctre) zc##O+6dx1sSNngN5(_8rgv`z2Kg79+sO%fM{<#Q_iw}zr>9_W&iOJoiQg4xQD*VH+RP5#ffadCoFoP! zvZ}wIL2H@g=PGiswdTu?3ba`}-P&^zxx(5# z{1X+}VQnA)hA8q*YZC=r531zA#UFz-BDyb=`M7`Qe^XI`|BA1Oe3eAc9HbDLD-tA6 zh-)aw5s(9B(GUGQ?+!H{Z?6Ls5qXQXt5vUyi)atP??m1Oq4@iM6hx6vMCHHL^|!=_ zM1vsMg`vuykas=32d*eW`< zf*|CYvZ6>Uz*Hp!bXsyvSy7}BIHec%Z&~&TOj$VJmKDui!vaB06oH9~!fDa ziz4t97H~O#Q5ZzetGUn<-zHXAx;0>?$lf7}1J>pUxQrr<200@nBtpNDAT;=QIS71( z1zb)PMw;oEG)#f7Vm&xlB=%st$e0fohsAggG@jiu_5NLe8P*oUG;1B|tuKRUG#w$s z{AN{h;EUbrU&e5~NCb9Z2RcOpY-w!cT$*k~y?}m&;a=y}D8-(A11Njgs%TPg^ z)&AumVgh;scJr(YCV5Q>vzFMlIY{aOBFt@e{*Rl2!l2GAwoMF)0zeH|0ts*&DVWVf z1WX2gBk<+TE)p00DD>iakWY%_LUc_*?2|#S&+~^l_;IA3k3~Bv0MP4$;Yk>Fhw0N)TacW~QD3HxC@5w(M z*d>yKKCT8qY{@}3GgAM4K_q9LN|D7{wJ|w=R#>1_|JI18|43Y~N`uR&@60^_4hBWD z8T{0VY#kQpNmUpgW)D25vKwT5P?t8ejUexb76H4)zl#$siLjhD z7DaJ^Y=-SDyGe$n-_W0)yX>$PM4BGBI)h(r{qhZBotXUcLl}y{C3@nD2#copVF8za z7mcx&;bAk^?BAI8K%Fq#RmYqyS-7?Z^P;P)1#J3Abn}6;A?i4|W1z&W!WD+kiqhvQxLnzu?(WYjzD*h$R?Wcg+b*! zxJGOj+2L1+cWU|)!wkYb07Ol48!Fy^u8L%6bHv37WOchEhwuItkO+j|Q*-6}MX$Z%)t5JJX)Zt0+egSYzCdgy%$inz-DRB*3Ds4V z*U;A921qs43SoUmyVuaM+up@7ECHNn8`pVD8lUsF9N@G$j&l}p&d&}Ec(W#4=GBbn zFgT8L5(qoLZsO(M10S5{jVKGxmqCODOkQ-lyT=5s&eJEo!`<-%twPRSTOlzoG6xt3 z4j(-a=L`S*l;I&=I~03=^4AX?ao<(`wB=aWi7`jIPkYCDy1ji}$Gk-iYm7*mVL5){ zq|5B`pBL#vshTo+T<^nmH+dJ8UtmP$uY0nnbA8plYD)|Qi9H}a;co#KzR=S4;?!{! zo)bBE>O|2O`a*LX@rOT3kXyxjpxD7lqB$>W*|qn|-rm0Mut*_)=hSW4z1Qv0>a8z> za1Q{xme<7bf8E+*LNg+(=6*J>b71QdRcY(~Cxbywa@*3lp>M{VI;PDHhmUtvUOZ}K zlh%MqV@Hhg%1WZo;$z32j;&jF>}GwS!d-g$&E^M-q*k04k!+RyWpi`w?vDL0o6-dA z`p^ERjhmY}cB5;5w27S;#nN;2;;ec>rpA~~-?RVqG2J~qn<^{DPbe)ZF=N!**Wc6l z(zZ>TT6Yq4S=m@iZr0PM&x^DG;fzd;V*}7xTYbeYE?V1Pd6&$kTnij8d`Zp|4@U9| z0Ey5;GC2O%M|i~ooFTDiULohh%ZsegJdp$pn7pWvV;`Ot_kZKslpK+13o`%!002ov JPDHLkV1jm((W3wW delta 2459 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 Date: Sun, 26 Oct 2025 09:15:22 +0100 Subject: [PATCH 08/42] Update --- img/views.svg | 1 + plugin.info.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 img/views.svg diff --git a/img/views.svg b/img/views.svg new file mode 100644 index 0000000..37010ca --- /dev/null +++ b/img/views.svg @@ -0,0 +1 @@ +eye-outline \ No newline at end of file diff --git a/plugin.info.txt b/plugin.info.txt index e441852..255006e 100644 --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base botmon author Sascha Leib email ad@hominem.com -date 2025-10-20 +date 2025-10-26 name Bot Monitoring desc A tool for monitoring and analysing bot traffic to your wiki (under development) url https://www.dokuwiki.org/plugin:botmon From cef3810678186559c94d70d74859b653db49d4f4 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sun, 26 Oct 2025 11:50:26 +0100 Subject: [PATCH 09/42] Admin UI fixes --- admin.css | 39 +++++++++++++++++++++++++++++---------- admin.js | 10 +++++----- admin.php | 2 +- captcha.js | 5 ++++- lang/en/settings.php | 6 +++++- style.less | 2 +- 6 files changed, 45 insertions(+), 19 deletions(-) diff --git a/admin.css b/admin.css index 8b15ce9..9acddbb 100644 --- a/admin.css +++ b/admin.css @@ -7,24 +7,29 @@ /* icon items */ .has_icon { display: inline-flex; + align-items: center; + width: auto; + white-space: nowrap; } .icon_only { display: inline-grid; grid-template-columns: 20px max-content; - overflow: hidden; - width: 20px; + width: 20px; max-width: 20px; } .has_icon, .icon_only { & { align-items: center; - column-gap: .25em; + overflow: hidden; + column-gap: .2em; + min-width: 20px; } &::before { content: ''; display: inline-block; - width: 20px; height: 20px; + width: 20px; min-width: 20px; max-width: 20px; + height: 20px; background: transparent none center no-repeat; background-position: 0 0; background-size: 20px; @@ -534,14 +539,15 @@ color: #333; cursor: pointer; } - &::marker, &::before { + &::marker { content: none; display: none; } &::before { content: ''; display: inline-block; - width: 1.25em; height: 1.25em; + width: 1.25em; min-width: 1.25em; max-width: 1.25em; + height: 1.25em; background: transparent url('img/chevron.svg') center no-repeat; background-size: 1.25em; transform: rotate(-90deg); @@ -607,8 +613,10 @@ border-radius: .5em; } details ul > li > details > summary { - display: flex; - justify-content: space-between; + display: grid; + grid-template-columns: 1.25em minmax(calc(100px + 4em), auto) max-content; + justify-items: stretch; + justify-content: stretch; align-items: center; column-gap: .5em; font-weight: normal; @@ -617,6 +625,7 @@ background-color: #F0F0F0; border-bottom: #CCC solid 1px; border-radius: .7em; + overflow: auto; } details ul > li > details > summary.noServer { opacity: 67%; @@ -627,7 +636,10 @@ column-gap: .25em; } details ul > li > details > summary > span:first-child { - flex-grow: 1; + justify-content: flex-start; + } + details ul > li > details > summary > span:last-child { + justify-content: flex-end; } details ul > li > details > summary > span > span[title] { cursor: help; @@ -744,11 +756,12 @@ font-size: smaller; border-radius: .5em; margin-right: .25em; + min-width: fit-content; } span.pageseen::before { content : ''; display: inline-block; - width: 1.25em; height: 1.25em; + width: 1.25em; min-width: 1.25em; max-width: 1.25em; height: 1.25em; background: transparent url('img/page.svg') center no-repeat; background-size: 1.25em; } @@ -980,3 +993,9 @@ } } } + +@media (max-width: 440px) { + #botmon__admin #botmon__latest #botmon__today__content details > div { padding: .25em; } + #botmon__admin #botmon__latest #botmon__today__visitorlists details ul > li { margin-left: 0; } + #botmon__admin #botmon__latest #botmon__today__visitorlists details ul > li > details > summary { column-gap: .1em; } +} \ No newline at end of file diff --git a/admin.js b/admin.js index a684ebc..eea32a0 100644 --- a/admin.js +++ b/admin.js @@ -182,7 +182,7 @@ const BotMon = { } }; -/* everything specific to the "Latest" tab is self-contained in the "live" object: */ +/* everything specific to the 'Latest' tab is self-contained in the 'live' object: */ BotMon.live = { init: function() { //console.info('BotMon.live.init()'); @@ -2306,15 +2306,15 @@ BotMon.live = { } else { /* others */ - /*span1.appendChild(make('span', { // IP-Address + span1.appendChild(make('span', { // IP-Address 'class': 'has_icon ipaddr ip' + ipType, 'title': "IP-Address: " + data.ip - }, data.ip));*/ + }, data.ip)); - span1.appendChild(make('span', { /* Internal ID */ + /*span1.appendChild(make('span', { // Internal ID 'class': 'has_icon session typ_' + data.typ, 'title': "ID: " + data.id - }, data.id)); + }, data.id));*/ } span1.appendChild(make('span', { /* page views */ diff --git a/admin.php b/admin.php index 2f30ef3..7a3c3a0 100644 --- a/admin.php +++ b/admin.php @@ -107,7 +107,7 @@ class admin_plugin_botmon extends AdminPlugin { echo '' . NL; echo ''; echo ''; diff --git a/captcha.js b/captcha.js index fdbcbc8..f95ddcd 100644 --- a/captcha.js +++ b/captcha.js @@ -27,12 +27,15 @@ const $BMCaptcha = { const dlg = document.createElement('dialog'); dlg.setAttribute('closedby', 'none'); dlg.setAttribute('open', 'open'); + dlg.setAttribute('role', 'alertdialog'); + dlg.setAttribute('aria-labelledby', 'botmon_captcha_title'); dlg.classList.add('checking'); dlg.id = 'botmon_captcha_box'; - dlg.innerHTML = '

    ' + _loc('dlgTitle', 'Title') + '

    ' + _loc('dlgSubtitle', 'Subtitle') + '

    '; + dlg.innerHTML = '

    ' + _loc('dlgTitle', 'Title') + '

    ' + _loc('dlgSubtitle', 'Subtitle') + '

    '; // Checkbox: const lbl = document.createElement('label'); + lbl.setAttribute('aria-live', 'assertive'); lbl.innerHTML = '' + _loc('dlgConfirm', "Confirm.") + '' + '' + _loc('dlgChecking', "Checking") + '' + '' + _loc('dlgLoading', "Loading") + '' + diff --git a/lang/en/settings.php b/lang/en/settings.php index d42003b..44d8c35 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -14,4 +14,8 @@ $lang['geoiplib'] = 'Add GeoIP Information
    (requires PHP module to b $lang['geoiplib_o_phpgeoip'] = 'Use GeoIP Module'; $lang['useCaptcha'] = 'Enable Captcha
    (Experimental, read the manual first!)'; -$lang['captchaSeed'] = 'Captcha Seed
    (Best use a random number, e.g. here )'; + $lang['useCaptcha_o_disabled'] = 'Disabled'; + $lang['useCaptcha_o_blank'] = 'Show empty background'; + $lang['useCaptcha_o_dada'] = 'Show placeholder text'; + + $lang['captchaSeed'] = 'Captcha Seed
    (Best use a random number, e.g. here )'; diff --git a/style.less b/style.less index fb29653..593404c 100644 --- a/style.less +++ b/style.less @@ -30,6 +30,7 @@ body.botmon_captcha { font-family: system-ui, sans-serif; box-shadow: .25rem .25rem .5rem rgba(0,0,0,.5); box-sizing: border-box; + z-index: 10001; } & * { color: #EDEDF5; @@ -112,7 +113,6 @@ body.botmon_captcha { height: auto; left: 0; top: 80px; border-radius: 2px; - z-index: 10001; } } From 0cbbfe53758b5a007f439677e0d3c87efa1837f8 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sun, 26 Oct 2025 14:37:15 +0100 Subject: [PATCH 10/42] Added Lorem Ipsum option instead of just blank page --- conf/metadata.php | 2 +- lang/en/settings.php | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/conf/metadata.php b/conf/metadata.php index 32dd6d0..64e2a86 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -13,5 +13,5 @@ $meta['geoiplib'] = array('multichoice', //$meta['useCaptcha'] = array('onoff'); $meta['useCaptcha'] = array('multichoice', - '_choices' => array ('disabled', 'blank', 'dada')); + '_choices' => array ('disabled', 'loremipsum', 'dada')); $meta['captchaSeed'] = array('string'); diff --git a/lang/en/settings.php b/lang/en/settings.php index 44d8c35..2cd4700 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -6,16 +6,16 @@ */ $lang['showday'] = 'Which data to show in the “Latest” tab:'; - $lang['showday_o_yesterday'] = 'Last full day (yesterday)'; - $lang['showday_o_today'] = 'Ongoing logs (today)'; + $lang['showday_o_yesterday'] = 'Last full day (yesterday)'; + $lang['showday_o_today'] = 'Ongoing logs (today)'; $lang['geoiplib'] = 'Add GeoIP Information
    (requires PHP module to be installed)'; - $lang['geoiplib_o_disabled'] = 'Disabled'; - $lang['geoiplib_o_phpgeoip'] = 'Use GeoIP Module'; + $lang['geoiplib_o_disabled'] = 'Disabled'; + $lang['geoiplib_o_phpgeoip'] = 'Use GeoIP Module'; $lang['useCaptcha'] = 'Enable Captcha
    (Experimental, read the manual first!)'; - $lang['useCaptcha_o_disabled'] = 'Disabled'; - $lang['useCaptcha_o_blank'] = 'Show empty background'; - $lang['useCaptcha_o_dada'] = 'Show placeholder text'; + $lang['useCaptcha_o_disabled'] = 'Disabled'; + $lang['useCaptcha_o_loremipsum'] = 'Captcha with Lorem ipsum text'; + $lang['useCaptcha_o_dada'] = 'Captcha with Dada placeholder text'; - $lang['captchaSeed'] = 'Captcha Seed
    (Best use a random number, e.g. here )'; +$lang['captchaSeed'] = 'Captcha Seed
    (Enter a random string, e.g. from here)'; From 393de67c2219bde8f8a5693bcbdc0755b6d8e697 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sun, 26 Oct 2025 14:37:35 +0100 Subject: [PATCH 11/42] temporarily log method for debugging --- action.php | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/action.php b/action.php index a14e347..759ee4b 100644 --- a/action.php +++ b/action.php @@ -26,12 +26,15 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { // populate the session id and type: $this->setSessionInfo(); + // temporary fix: save the method of the request: + $this->tempMethod = $_SERVER['REQUEST_METHOD']; + // insert header data into the page: if ($ACT == 'show' || $ACT == 'edit' || $ACT == 'media') { $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertHeader'); // Override the page rendering, if a captcha needs to be displayed: - $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'showCaptcha'); + $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'insertCaptchaCode'); } else if ($ACT == 'admin' && isset($_REQUEST['page']) && $_REQUEST['page'] == 'botmon') { $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertAdminHeader'); @@ -48,7 +51,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { /* session information */ private $sessionId = null; private $sessionType = ''; - private $showCaptcha = '-'; + private $showCaptcha = 'X'; + private $tempMethod = ''; /** * Inserts tracking code to the page header @@ -133,7 +137,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { substr($conf['lang'],0,2), /* page language */ implode(',', array_unique(array_map( function($it) { return substr(trim($it),0,2); }, explode(',',trim($_SERVER['HTTP_ACCEPT_LANGUAGE'], " \t;,*"))))), /* accepted client languages */ $this->getCountryCode(), /* GeoIP country code */ - $this->showCaptcha /* show captcha? */ + $this->showCaptcha, /* show captcha? */ + $this->tempMethod /* show captcha? */ ); //* create the log line */ @@ -204,7 +209,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { } - public function showCaptcha(Event $event) { + public function insertCaptchaCode(Event $event) { $useCaptcha = $this->getConf('useCaptcha'); @@ -221,8 +226,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { echo '

    '; tpl_pagetitle(); echo "

    \n"; // always show the original page title $event->preventDefault(); // don't show normal content switch ($useCaptcha) { - case 'blank': - $this->insertBlankBox(); // show dada filler instead of text + case 'loremipsum': + $this->insertLoremIpsum(); // show dada filler instead of text break; case 'dada': $this->insertDadaFiller(); // show dada filler instead of text @@ -338,9 +343,13 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { } // inserts a blank box to ensure there is enough space for the captcha: - private function insertBlankBox() { + private function insertLoremIpsum() { + + echo '
    ' . NL; + echo '

    ' . NL . 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'. NL . '

    ' . NL; + echo '

    ' . NL . 'At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga.'. NL . '

    ' . NL; + echo '
    ' . NL; - echo '

     

    '; } /* Generates a few paragraphs of Dada text to show instead of the article content */ From c840f85d590fb5f403a1e18a9c54c2acfc6b1ae0 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sun, 26 Oct 2025 17:16:22 +0100 Subject: [PATCH 12/42] Added HTTP libraries --- admin.css | 1 + config/default-config.json | 2 +- config/known-clients.json | 8 ++++++++ img/clients.png | Bin 23137 -> 24006 bytes 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/admin.css b/admin.css index 9acddbb..c3fb2ba 100644 --- a/admin.css +++ b/admin.css @@ -111,6 +111,7 @@ &.cl_ecosia::before { background-position-y: -340px } &.cl_webkit::before { background-position-y: -360px } &.cl_operaold::before { background-position-y: -380px } + &.cl_wget::before, &.cl_urllib::before { background-position-y: -400px } &.cl_other::before { background-image: url('img/more.svg') } /* Captcha statuses */ diff --git a/config/default-config.json b/config/default-config.json index dbe7b86..e9e28a3 100644 --- a/config/default-config.json +++ b/config/default-config.json @@ -41,7 +41,7 @@ "id": "susClient", "desc": "Client identifier that is popular with bot networks", "bot": 10 }, - {"func": "matchesClient", "params": ["undici","gohttp"], + {"func": "matchesClient", "params": ["undici","gohttp","urllib","wget","curl"], "id": "botClient", "desc": "Client identifier indicates web crawler", "bot": 100 }, diff --git a/config/known-clients.json b/config/known-clients.json index d0141ee..04ad822 100644 --- a/config/known-clients.json +++ b/config/known-clients.json @@ -91,8 +91,16 @@ "id": "gohttp", "rx": ["Go\\-http\\-client\\/(\\d+)", "quic\\-go\\-HTTP\\/(\\d+)"] }, + {"n": "Python URLlib-based crawler", + "id": "urllib", + "rx": ["Python\\-urllib\\/(\\d+\\.?\\d*)", "quic\\-go\\-HTTP\\/(\\d+)"] + }, {"n": "AppleWebKit", "id": "webkit", "rx": [ "AppleWebKit\\/(\\d*)\\." ] + }, + {"n": "wget", + "id": "wget", + "rx": [ "Wget\\/(\\d+\\.?\\d*\\.?\\d*)" ] } ] \ No newline at end of file diff --git a/img/clients.png b/img/clients.png index 6e59da96cf0abc0568b735d49db93ac3e0bb3223..c96355fe5d1f5de9cf2a5a9ed42bac83a3bce934 100644 GIT binary patch delta 23901 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 delta 23025 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 Ef|)yG*#H0l From 19b69b647da12be70cee3430601d5917bad20dca Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sun, 26 Oct 2025 17:16:34 +0100 Subject: [PATCH 13/42] Captcha code reorg --- action.php | 111 +++++++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/action.php b/action.php index 759ee4b..ea97afa 100644 --- a/action.php +++ b/action.php @@ -13,6 +13,26 @@ use dokuwiki\Logger; class action_plugin_botmon extends DokuWiki_Action_Plugin { + public function __construct() { + + // determine if a captcha should be loaded: + $this->showCaptcha = 'Z'; + + $useCaptcha = $this->getConf('useCaptcha'); // should we show a captcha? + + if ($useCaptcha !== 'disabled') { + if ($_SERVER['REQUEST_METHOD'] == 'HEAD') { + $this->showCaptcha = 'H'; // Method is HEAD, no need for captcha + } elseif ($this->captchaWhitelisted()) { + $this->showCaptcha = 'W'; // IP is whitelisted, no captcha + } elseif ($this->hasCaptchaCookie()) { + $this->showCaptcha = 'N'; // No, user already has a cookie, don't show the captcha + } else { + $this->showCaptcha = 'Y'; // Yes, show the captcha + } + } + } + /** * Registers a callback functions * @@ -26,9 +46,6 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { // populate the session id and type: $this->setSessionInfo(); - // temporary fix: save the method of the request: - $this->tempMethod = $_SERVER['REQUEST_METHOD']; - // insert header data into the page: if ($ACT == 'show' || $ACT == 'edit' || $ACT == 'media') { $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertHeader'); @@ -52,7 +69,6 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { private $sessionId = null; private $sessionType = ''; private $showCaptcha = 'X'; - private $tempMethod = ''; /** * Inserts tracking code to the page header @@ -137,8 +153,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { substr($conf['lang'],0,2), /* page language */ implode(',', array_unique(array_map( function($it) { return substr(trim($it),0,2); }, explode(',',trim($_SERVER['HTTP_ACCEPT_LANGUAGE'], " \t;,*"))))), /* accepted client languages */ $this->getCountryCode(), /* GeoIP country code */ - $this->showCaptcha, /* show captcha? */ - $this->tempMethod /* show captcha? */ + $this->showCaptcha /* show captcha? */ ); //* create the log line */ @@ -211,33 +226,45 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { public function insertCaptchaCode(Event $event) { - $useCaptcha = $this->getConf('useCaptcha'); + $useCaptcha = $this->getConf('useCaptcha'); // which background to show? - $cCode = '-'; - if ($useCaptcha !== 'disabled') { - if ($this->captchaWhitelisted()) { - $cCode = 'W'; // whitelisted - } elseif ($this->hasCaptchaCookie()) { - $cCode = 'N'; // user already has a cookie - } else { - $cCode = 'Y'; // show the captcha + // only if we previously determined that we need a captcha: + if ($this->showCaptcha == 'Y') { - - echo '

    '; tpl_pagetitle(); echo "

    \n"; // always show the original page title - $event->preventDefault(); // don't show normal content - switch ($useCaptcha) { - case 'loremipsum': - $this->insertLoremIpsum(); // show dada filler instead of text - break; - case 'dada': - $this->insertDadaFiller(); // show dada filler instead of text - break; - } - $this->insertCaptchaLoader(); // and load the captcha + echo '

    '; tpl_pagetitle(); echo "

    \n"; // always show the original page title + $event->preventDefault(); // don't show normal content + switch ($useCaptcha) { + case 'loremipsum': + $this->insertLoremIpsum(); // show dada filler instead of text + break; + case 'dada': + $this->insertDadaFiller(); // show dada filler instead of text + break; } - } - $this->showCaptcha = $cCode; // store the captcha code for the logfile + // insert the captcha loader code: + echo '' . NL; + } } public function showImageCaptcha(Event $event, $param) { @@ -316,32 +343,6 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { return false; /* IP not found in whitelist */ } - private function insertCaptchaLoader() { - - echo '' . NL; - - } - // inserts a blank box to ensure there is enough space for the captcha: private function insertLoremIpsum() { From bc52cf5354998ddadd21f3ebe32fef84ca579334 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sun, 26 Oct 2025 19:21:07 +0100 Subject: [PATCH 14/42] More clients --- admin.css | 5 ++++- admin.js | 37 +++++++++++++++++++++++++++++-------- config/known-clients.json | 6 +++--- img/captcha.png | Bin 2740 -> 3382 bytes img/clients.png | Bin 24006 -> 24898 bytes 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/admin.css b/admin.css index c3fb2ba..af620f0 100644 --- a/admin.css +++ b/admin.css @@ -111,7 +111,8 @@ &.cl_ecosia::before { background-position-y: -340px } &.cl_webkit::before { background-position-y: -360px } &.cl_operaold::before { background-position-y: -380px } - &.cl_wget::before, &.cl_urllib::before { background-position-y: -400px } + &.cl_wget::before { background-position-y: -400px } + &.cl_python::before { background-position-y: -420px } &.cl_other::before { background-image: url('img/more.svg') } /* Captcha statuses */ @@ -119,6 +120,8 @@ &.cap_Y::before { background-position-y: -20px } &.cap_YN::before { background-position-y: -40px } &.cap_W::before { background-position-y: -60px } + &.cap_H::before { background-position-y: -80px } + &.cap_X::before { background-position-y: -100px } /* Country flags */ /* Note: flag images and CSS adapted from: https://github.com/lafeber/world-flags-sprite/ */ diff --git a/admin.js b/admin.js index eea32a0..00cecc9 100644 --- a/admin.js +++ b/admin.js @@ -389,7 +389,9 @@ BotMon.live = { _jsClient: false, // visitor has been seen logged by client js as well _client: BotMon.live.data.clients.match(nv.agent) ?? null, // client info _platform: BotMon.live.data.platforms.match(nv.agent), // platform info - _captcha: {'-': 0, 'Y': 0, 'N': 0, 'W':0} // captcha counter + _captcha: {'X': 0, 'Y': 0, 'N': 0, 'W':0, 'H': 0, + _str: function() { return (this.X > 0 ? 'X' : '') + (this.Y > 0 ? 'Y' : '') + (this.N > 0 ? 'N' : '') + (this.W > 0 ? 'W' : '') + (this.H > 0 ? 'H' : ''); } + } // captcha counter }}; model._visitors.push(visitor); }; @@ -540,6 +542,19 @@ BotMon.live = { _loadCount: 0, _tickCount: 0 }; + }, + + // helper function to make a human-readable title from the Captcha statuses: + _makeCaptchaTitle: function(cObj) { + const cStr = cObj._str(); + switch (cStr) { + case 'Y': + case 'NY': return "Blocked by captcha"; + case 'YN': return "Captcha solved"; + case 'W': return "IP Address whitelisted"; + case 'H': return "HEAD request, no captcha"; + default: return "Undefined: " + cStr; + } } }, @@ -2255,8 +2270,9 @@ BotMon.live = { _makeVisitorItem: function(data, type) { - // shortcut for neater code: + // shortcuts for neater code: const make = BotMon.t._makeElement; + const model = BotMon.live.data.model; let ipType = ( data.ip.indexOf(':') >= 0 ? '6' : '4' ); if (data.ip == '127.0.0.1' || data.ip == '::1' ) ipType = '0'; @@ -2349,11 +2365,14 @@ BotMon.live = { 'title': "Seen by: " + data._seenBy.join('+') }, data._seenBy.join(', '))); - const captchaCode = '' + ( data._captcha['Y'] > 0 ? 'Y' : '' ) + ( data._captcha['N'] > 0 ? 'N' : '' ) + ( data._captcha['W'] > 0 ? 'W' : '' ); - if (captchaCode !== '') { + // captcha status: + const cCode = ( data._captcha ? data._captcha._str() : ''); + if (cCode !== '') { + const cTitle = model._makeCaptchaTitle(data._captcha) span2.appendChild(make('span', { // captcha status - 'class': 'icon_only captcha cap_' + captchaCode, - }, captchaCode)); + 'class': 'icon_only captcha cap_' + cCode, + 'title': "Captcha-status: " + cTitle + }, cTitle)); } summary.appendChild(span2); @@ -2367,8 +2386,9 @@ BotMon.live = { _makeVisitorDetails: function(data, type) { - // shortcut for neater code: + // shortcuts for neater code: const make = BotMon.t._makeElement; + const model = BotMon.live.data.model; let ipType = ( data.ip.indexOf(':') >= 0 ? '6' : '4' ); if (data.ip == '127.0.0.1' || data.ip == '::1' ) ipType = '0'; @@ -2468,11 +2488,12 @@ BotMon.live = { 'title': "Country: " + data._country }, data._country + ' (' + data.geo + ')')); } + if (data.captcha && data.captcha !=='') { dl.appendChild(make('dt', {}, "Captcha-status:")); dl.appendChild(make('dd', { 'class': 'captcha' - }, data.captcha)); + }, model._makeCaptchaTitle(data._captcha))); } dl.appendChild(make('dt', {}, "Session ID:")); diff --git a/config/known-clients.json b/config/known-clients.json index 04ad822..9f35745 100644 --- a/config/known-clients.json +++ b/config/known-clients.json @@ -87,13 +87,13 @@ "id": "undici", "rx": ["undici"] }, - {"n": "GoHTTP-based crawler", + {"n": "GoHTTP-based bot", "id": "gohttp", "rx": ["Go\\-http\\-client\\/(\\d+)", "quic\\-go\\-HTTP\\/(\\d+)"] }, {"n": "Python URLlib-based crawler", - "id": "urllib", - "rx": ["Python\\-urllib\\/(\\d+\\.?\\d*)", "quic\\-go\\-HTTP\\/(\\d+)"] + "id": "python", + "rx": ["Python\\-?\\w*\\/(\\d+\\.?\\d*)"] }, {"n": "AppleWebKit", "id": "webkit", diff --git a/img/captcha.png b/img/captcha.png index f101af1f226a35bd40e4cd2453def0b4f18378b4..842decfa21265918eda71867c9c93a8fabd7b496 100644 GIT binary patch delta 3156 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 M07*qoM6N<$g6l85z5oCK delta 23902 zcmV(`K-0g%!U4v+0gx680r&_80000`L@my-AsPXHTy;rAK~#8N#k~id9L3Q-ToX23 z&PgYo?vxYCC;>_c0RlwMIU5_WjY-CUjLBdd8!%up$bbQpK|~=Tgt7$6@suv-&1u5- zRL}0-?%hg&{r-R7Bh^jVnX2yU>gwvA?iq;=gIjOCwFH}qpb0S=4`QrMqSX&Q^iUh0 zOoz080>IdY%^fjP`P^X;0sRpKIS=XLOcHtt0P~U9ybKyAPNbqewk?cpld&3f350C7 z4lT&BtpJ!`iOo7tocV{Kd#qi7$3TCxb_MPNeQfR0k)RFWaj|u1At4}^@+U!d&_U2F zq~;WCj{-3~peI0&$MzXham`8C9t)}iIYG~VfY(rL1%v>cU_G;1n7J1;4>x{|^8>Iy z6U2BD=en4%{}1aF9fxcB;@nr*XF)y?3wasm6>JkZ_&tc#Vt~%W4V(BRz$)W%3V{B? zCt~1zKF9Z8#wT#k57?XsVjirnTRk`VumbG(ax zPsA_;lnG+%>cax&Vdo>z{|lU`9Cg%D=VNCEXdmb;(E1oPf;g3ow?WH6@!y#Cf`)?_ zY;E6w;=i#O=Ye)`4){kdpr@h7UtlYM^L&nfV^ra{1F?Mp{9c6(CyH?revg|g|MoOC z*ML6YLW);^PEj)QV_QZ#SI`>UG74gU+c@I9CM4>?|0}Qnc36z+*fx&N*p^Wg+a{wp zwqrS{>dwxJ_4x&#jiDUG<-p)VP8P^u6`pDBin405b_M2u`h)Ib0ViT7 zQ9%F4IFVJvUXUvVgDYCHDRKp4@HY8;@^4(xlFu^_2J>7a!@Sr+SVasM=x&^U8_$G& zc5Vz#$ni<%Pl%mkJeqWV0f>1p7T{c4{Gx|IoD)8N9cnv*PXf<@Oi%{sH+&)n-sf}d zcV6TZxMvJDuYs7yD$v8A?gj@Ju#4RQVsFU4=~ATNE1YLfISa%tnkyIIvmcZV`V`lk zfNQu=Sx_HP2s9SwMZ=TQqTxb+ZY-Vwu@D9qJ~u#YX$;=-J#m%t@e~lNjKR#W2J=^~ z!yW<{v7B(*z%I8ZxXI*ude&S9tA(4)WN%EOQAoz}<;&aP06Dv1(9xjeYgq5`LX3m0 zxEtd z%;#zxvE5!USC6eg%8!N+A`N6fh)r>jVJ5f9DM%4uK2Y@@Mf7)Lhh)AvEH%1|>UUFE zgMCFo*v5rqIxEfflirDcU6q1_0OPu$Z&877K84I;^TzRt`L}T^DC$ZG6VUV%DS)~Z zfCWcb2#9lI!kc7eoMx;%<~U>f$^VR9Eg%F$&YpS>mAlUrk&Yf2L0QHD3TI5Fc18TY z`mBdfU4F(3?`>Ojc3H>fYBD@v8bJ<1pa`oA7bz-9ru3E>jh=6RY@PdOzKERq{l0#C+A=^oRmYlTr%W2lNobv$G6qQI)ZTaL-WHX|RAX%2x=;KlZDbr;#A;9n# zk{KvNE1QF+BT9B>E6q9QDmrT3b);uSC~UV;NX;ZyPCNBKWhI#$+i?>k2m(mqKP-$r zECeW#Q6kkr8|fJ?l8++7=@7d*r6mMo&I* z7v1~b6ZFJ?$_Hp|*Es5O>R_yr$DvYKHpv~x1!I`4KTM{ik?sW_l}#8q@>xz;4!0L{ z3IR+3bPpg%37WKG*$mp;RD@>CE2vZPQI{H^PI~~Yibg@wDQrZ^^nzh0$}eJ)scJAX zx0MrbOhBNeXg@`95QLgE=#hnBzs9kT0)`*u4E9=o7kPDT4JM!1_v;~&lxC7zaeb#s zM!SQQe1t}@CcS~riQ0i2cyqRD9yG6h6si|Ki?==pm9}-!!19Bn`#q%j+!PFUPyiGR zVH@fsf3TBg3`3=D!}V=2TW1GJKIk5WY_{0p(lriYP{^kGzNZO&5SXb|*dXLcK@)$o z<3BWipC zO$HL@0%b@PZYm>`zt8^Y`y6#x0_w8=FfJ;8wXH4b+a0GAc#PQf`1y2i1mEr$#ZE~wbIJ#)pshgsY#qzGWtAt##TzF1R( zQYLikaBAdqM#%06z{Yiui8e+iU02BKc2UTag$&xsnH5^C=J}7#-1P5wAT7{K00U(* z*)#)-;Fj|=_!0xPl+~fYkLeoR8JZD)#e{1yRX7Bx)#iwvo8R>G;@IseNGl+Yx~x0V zvDQvO)j>R@^?^Q6QB*i(rA3mZZE(sTmboA1p@l=@umZZex+)zG$C0uuk3|46&M;}5 z32&yzlIRQQy1pSC4zJG3%i~};iJbkk9Kq|j;HfZTHzKp-KYt*vcb zSJbxR%r_+a=n9qc-~!%;W{dL-Itb;PDoC+ZBpDLPU|MODsHID$v(fEncgs|Jz)i1Q z;ELqfB9n`Yi$zX^fc^XTJ2JDfmf!J}ZG6PA(e98*btp{DWuSpUg<{ozAms)soXj}EfwN__;#C(Myc7kZZ^dJps*+8#;c`NNln6uI-BIfZicx7Z{+|Jia2M!#l z^y>E78$Pw$%CenEHPI4(uYoYdAOk{JHJoy0EL4P0Q4Z_m@O$a@^&ilM+m_Jt`Ge>$ z6RxKj<@scfIH?Iu=;OynqM1^tMuvSKxvGwAS!g$Qc1g4h1!{m1GZw1hf3aK`Mg)G| zPh$>jpl?2Th%VT=n3fnSJvQk)gfE%&ZHJ9^cqPg~o4p!56-AkU$SNAUy94!8anZcQeiUqN1qWjX@Z#y9Um zZ_Y4`aWZP-xLQ~O+!w8=!(aZLFJZe!%E>ffh7s!TZKQ8Me4K9Gy_%W=LE7c_(+i`I zr9A^jk>o&QfY_9OOKDj>+l@rK5eNtYQOy{~2OP?jWm??DU%n3JNgBLq&_gdz?MF?g zpG60QL8=Y-X+^~l`e6LYXp3{<&TYbUIir=3CF9;booIPAV?5^o!IFl+7A_3@Bpo&` zoF{J3Z~_eP8|0!vc^OnburIwm>1b-n$e};ax{Tz^0x;))kx*RA$%hcmh5-{tsc{Zi zn6bVpH;U#?qk||53GP^ezm&r!NodH=DA4Jg0R^;o?_R2_tE2q!<7vT3H&Q6SFB&8G z5gF>`pcnV?wz12~K}{|snff};!TSDL8YJp;bbpg{jQFNT3ebJy3Te-tJyhS= zM4usI_-$K%DIF{s2EDt`_^1|hP7tC(8*)FbpNXzRnEGW$P*3#rEMP;wtS~w+5n5j# zB0Z=hvV&0s!}P?2Ose0zhc?$W)1MAH>BVqAGP29yt}{`iZBV7fSP6pQ^voc9R3kTN z67|ahFBYI_+Ug?;!ci|O&%gqMrdCFgdW~)y2^F<}?W4E1x#{LzCA3rNPl~eu%#m`m zWJ&TT0@lc5$BwPbv+EC>+^>teLQ&e^6{U+t z1*p<&px>hre0g7Agy@xI*Yd~~bdk+t10hKEC6QmIp#>W5)u=P55%bEiY4?vAGX{pw zg>M*t_s=X3uFF&WG_E2{nMN1g{B;KHMMuOYgCf~b0lHA|Gd2XaDqOh@!7Ve-Ks;+h zm&zXrQdM4<@(}#aC=ad$&j*;d5HMlFgs@@gS6x21Bl68|KdtY~qFKXoD57Lic{bN# zXw|C=hC&@^7o?CuWr#R@VILhoP^UgQI;}Z>=%>qvbVb1HYUatjxjH5A-FM$z(-~I& z{=&{2dk3O48*IEJOQogz;ZMpWI^aeWZANK8fkJERO`6_cqb46J4eUNw@zUjkx*`Qm z^ZF@MrX;$uDFPVFmoFb{80ND}8lB@8H5O8~(?(AC7#$T4P6Ia1VGCCzlfvi)dC|jv zIiaG1W|Rfj%CdCTv}x16kFgTyC4hmPIAC4B?+8h^Z|-v1>H-crhx+j%EvN{W1rWLM{ZU|S9SR7r|rn1-?2jA*;CU1e(ezls)qz&+)( zNGqV_j1%%CMY>;7m5U@<&O&2>kRF#J^S)=uh)CrdeSc<~L0z&OSHBP9Twk#@WZrkW~3I zgwS?utrW9LgnD=%|KXZ>D;SH;HujMSlbb=_^3V>8)AVD?KK+^>$yhm<`}W-Y^`-j5 zxQts5>taqBGm709&xH|zYv*HsPIc0CiLIu~*nhe4*y;Qv%F4kB?;U@qOEb>7{|W!_ z9J@`DS?8?l_!cQg>O+RpvD+*N1(d=jNs?JtNB|(1bHRvH);V&87nS^B?*i$#H|J)m z8Pa}PkqYO2>RUPY2V-&$A_P&Rm}J=-J?XUFu9N2vcWY{xPi3hAhtYh0&klRw^`WS} zR;~foAZt^BZOF12kW8giI{uxxSE^3=S)pUiX!_${-8tE&nvsp3qbW(==pP@7tT*zr zmmk#ZstxC$8UxMi^#1nnsGJS%0`Elys)~~0;sK8yxJVgz?x4$Mn`Dhs%TiWTHmw}F zEmEZ;T#24?#`fj;L;8Gwu%*kcL*E(#gb>Og5RTINgB~?EtFKa?(YifL-d>u9IU~%z zEB-J}VCyL7#O4g6DHzoQj`^n#eSK}C-G}DW8w`=#7a*@cKt9Ap0km4LtZHyZb8q8E zi-$rLThSnE-vVl?^M$iA8w`i@z`piC$n6i356r#Tdcar^pF>oC*X|8aqt_YM3^n-y zQ8N0;D0Ck4Pl3DAId1)^tU66MOg~c3%-l#h=fL9&k~b!Z&qH-E966=yngq9uLR$}Y z_rkj~waSV-yDJh2BgL$mpb!uj#K$HIyEu~^HYFm4&(dHGDg|v|%jgNyArJks{mZqN zOzj&IDP|Q3iU~k}>aU$q70eDkz)wh2>#kIohP)O6%nb%wJ>kH5U{UA$1IG?FyRM%- zAQX*SIdMZpI%23>jvo{p+P9@G-+XgK?DAePqPDft>{sXHC{B5=>W~UxuCbp|&^jaM zxZNidoiMK19SJF@O<5V5^TX!%*PZVChcP_U6MyEP0zKk?a2pXjT5{mn*N(efaY)a@ z%^}85Z_t3(F4+(0rtS&{!_|ZAp51@#w~?Ru3xAdjLql*7aa`t{-1bs}8-y%IE@pBr z4v`Z96A`x_HE3bgcboET`l#4x0q7jeZMa7L5X^Z-L$`DzVqT+#%@BxmkZ{3iz#;j` zne{7k6?2q-iAclLl~{`HxDS22ki8@5Q?8C(-rXKBX1+M*SXrfi$#$s(&QCOxJoe2b z0GUcK>|a>E+2^pErJUk8V6ha9N*$4qbbjf)rq5!hQw1<)Ja=q4%Heg;bcy?FEFk7+ zI9JzE?Ke}heaFNG;sxNM-~j1E2)MfH48-|KNUQ9BW1gO^K)@f6@@vo@l*g|siUbcV zu+^5SD%q0+xWW9_gDz-zNgVY+S^>#8`l;DRK*(IQBy(@{)Ea&4(Nfh$Ytas^ktB01 zg8Yr+eo=QQS8;d&qn2$iMv+g#W#g?usmOSVqM7J+?w2S83U7u-er4lPXKst#(hF$? zjQys64*fyxR@l!hx^&c zEdfY54>$2cX7MQ3(4rJ-ZzbQpItn&4krw9I)~eZD7Pvy}^Ej#urgCg6geV$iIzv@| z*{+aya3_*Ferd}BsNn8EbBo^k!*(U?3&ZKzL=3_WhJ;>?GtW-0etG1qF6CPwE`q}K zi07sbo12^{3#ZKoH3hNd!q=&}D|3=G=9_)_WGIad-)yu8I--)CnL|njED-__T_FXq z9CWND^u^4Oo9wwnd6TQ)5DijQA5_wRA(%UCUTpnBfWsC>!AGgxW6HEgJgKvOZXDSh;tWyICnvi;yZ;HZI_cK z)5_<4|M_*o1v9E&e&>sv0ik1UtQIu@m9T0;s1FgCtR@OvH}s&a*^5V8kunm@TQ(L5 z;UHEI_)Kl>V%ZEu#UzYmNUHjOs3iHI4jz+WiV!-ENWBjA=*WRa;fVGPk*?iEZSgkhk~ z6{>1Ehyjl;gJF0UMx(w@Q8LAon%<#T-1y3>g$&k$lMsy$tPl&FM zV8k1!$dSc*1Bs3u+=bzkO!J$ZGoJZ#?rBdY&tU6?yyNao%zY8Qh;G~LK+0Dlbhk-A5qBB@UEX2NZIVsU7Nj6v#$0e*ktHMB-P@4!R#J)JjZ(7u6XrVct zFYa2&Y()tQJUDV2uK5Ls{jpF}v}_>ie;7CMh!oC2fCPr(cNP|BZk@q3a@5p9=f;^c zx&<&E9Qg|V9*Hu45o10^c8MJBcLX}tL3T)74rFq?0tm*i4k3thB}oU&ur>Nvcz$C0Y8F% zUv!|Vi}}81fq0q&Pk7n`VqFO;-vjYHRi1Ri zLXY@S0bt6rnePJ~1L8@l>A-R6RYK17yb^7&e1>N_VvT!Fa4 zu&_7~ai`2RJrsCeHcxX;#^Rm=SUtTO`2PX5$tpS<8@8bTON500FlYbE^F(85AG~bm~+JtixCb1o^tzNLxKPv6?q?o$Uw{Z z;CRqHocILC+-R_~N{4Sj>USXa zqW>-K1TR*b@O%h9X(u!16WH?PR8AeYt^W-Y%o!|zSEn3-l#B6xMoh?LP$Gc(3E<@e z+@d9zGgttx9pLC-0fca4!v)6`?nV&%dtR!P@?53|c=kRstp;;miIf5^1Fmcjg3bYd z@r*^DGO`==HEs~?D+AZ@AUZch=;w;5>pZue8=zgdE;g&IH&UEkPndm1@iGjq7d)Yx zr~fl?W6q%^Cxo}(NcEcGG6#w&i z=RKJ-Ql^+64oLZ<$ce}SZ<%a=s;VGoXRrXSJn678w*65Yxo1y3RMW!hVFF`85Ofj? zh%dWH23x`wYxk!Csoy1X5zB!PX17-#DhGR@|6Y&34hIzKE>3m4poO|rwGRtOl*<1C zq}oiRJVp>GhEl}r;vN_P>tc`jlL6*~kqg0?1;vDkxh?4gaIfZ&kFU~y0n7)*84Fce z`+Mb}ZmbXLIt4(yjZ;WB;tQ^R6p^`e`#|RHwGaj#yl>6?7!c1%oz@W z$!jthMkEsbA`l4Qm%n`Jve-F+UIG~JE2R!uqK6>hmpBxokqJno735{@%FW5}D2lAR z-F}xp82J~NKYohd{Ns&>xvfj`pS^#mAs5rXyEJLTDF`zsK?SIOecr**Rt#j|im2 zVFj3T^G_v@^Bt0Z?L1|iD9U4ZFgzYY3kI--xJdYPX^+kkd0HG&fO$gUcnW2&qKLs$ zy2O|rMG?ShAO%{B3q)*0x;{zT73D|oy^tcnJl#HtqS<#*z&4(AwSaWgd(;P!VlMFr z2jIAcM;>{e3Fm@^MM6=8A04D=5MtgV?Gp%=?^D zo+0JHu?*_y0#lr0AtDzL9!@kLJj!wcgm6ct8xT+rG6kJ2ndWzInr0+qnyWrD&G()$ zO`jVYkHL%mSDrA<~-W_!NWxR(e~${UQLDH8^aYC_~ujeD*g>swGs`5SHA|mV1Dj) z>@cE6?vc?Fs3`sd0l~Zsq>I#l`+6)%G3@xxG#@$5G_UR-I{{3qGRbs!muM4$@+)vpC^2+x@dAY6|{8Y(XA>_S9I zYp~CMD~t23UR*sol#jfEzUf5tE!)u@ABt_t^4O`!fzU{D4mf;gUvI&O4yKq@0}KG8 z`{0GSl5x#_1N+RJ!LGxdGWf_mkRl5S0VoIN)~)=!GW7qgN4o+p^w$+MVh)5$ncR1e zp%~W{C0$3f6seD;EHwv=FJoJ{i6(SVWBu@d=33+cI{x8nq-`swrV4xhh8`T|EyK;yILKO$lQqDo!GwTXmg!8XIPxK1f`k}b|2WZ15EHaK! z$N`UI3-lDg$2>gWo#%{PqQ}uM=R!{g7wYtZ(71RW7e^DAr;NU&s0ii2=}TQi8vp}; zHrw_j|J8&J(d8%gWX|Zd05>HEj9%s%M-F;gzd(HmBMra32<5^9WW6vAIpCq71bPWz zK*-(L@LL1A!F4Ev%Xv(t3qrVyl8U>*Joz0j0zIur0IK5Absp{QF$RCEN?4xb-bwV2u$ z{*4TOcaxC}=g=`!c-hU=^w6(a$hde;L~kah^N&k!yuVN98~>otb_C7n)J1n~q9F9F z<`+`-lw&D#@@xw4-b#8$?=scenkZVomkQ7SmF!#o+2k8XRJ`!_t&IqOYylkH37wzE zT_)&K+<LQH+&1VrB-Pc;utuB)WVYT$le_q z1T2-2`VzJlF3=97J`URn7f{|f9z^c}2zDNQ66K%EeUskkc>PJrnR5n#=bg-(Yw|=k zMYa}GM9&s%qmHCDcYwZsUbmMbJf|lKOV;DuQKy4R@0VdQ59Z|>H4%-s^>UsGZ0Br> z$uR6r%FA-n%<Ku4o-CA@sMDa8FZ`ZlAyCILZ5jfb*^caW=pcj#hAnTBh{n{8qOP+Qq?r2u`eNR#MHbi>}C zn%4nXY)2_~K6c|5-I&y8a`iNY5 z?(DwQp4UBpXzUy|mTYBx$-DS{9tX6>uK5q6m%A1oRi*qVK1M8AM}OOJ9}o458`vqv zan_$+YfccL%`pIB_j*S(vFr^P$)`IcR3tO%|sx@CCiJ$S0GRZ7Q zp>oWBUxsk840mX_sR;$%h-;egdlSyD#5M0f^=(nA#{^OYTs^fl8wSEtPA z1+lBFcA@(z2m1E-3H#P{9-My6FzHYK{H{3BM~mG`bmgSxVYu@HTr`kx^9^o2gRZb|?97b5M8)vlG8f z2Olx^EnPCcksAVj3YD0@$f;s*%4CHaXP=d^d)y31;eb*0fm+mzZd(`L{r&+hKMTiXX3L53 z>z1HNFYk%cPQNZ*zQHTvbzT?3$y|gM@bwTg7r6$;%Z6?a5Gvsrvz%@I0spIqtIO_v zz4)kP=6ldLdvsldYP$@Qb%_SKWV)q)#%?-q@@!Jjcay&eTQiq6L-0cm2FPIEpY<8n zt(YxKM*Z#26s9~p+Sp{!Gi#$1LMy7ECd@2R=#~)<8lI<0cfDAcOKT&V(G*+CheUGv z!LwTCKg?Wc2xnQyNtb2qb!EwkMtaqLgZ{N%7h@wT8ujxAsdO%)3Lc1L^c`q_?>Asm zbVWR&BC$G8Q#5OMQgMHlDt^F8-cFd|OvEW$>rMJ_1FM9W>Phs=u{N64r}xCQvD2I? zZWTFTK`~)g4)}-oN#rh6J=|WyH`TSF>%KC^9Lc?Yib?|tV#)4-F-JQqIDtvbnHO@v z36K9^36c#QcA*>a*4M~pL?Ro1!jTJ(RH!gBJ#&T@F*UXiW}FbB8@h}?wMi#kzvX7$Seg149hx~~-O^xFJfSRz2?^b6&AX1ND8Bvr8$)SmfZ4$mCk&Dh zCLW$SW7U@dVfswng3Ns~wzZrsKFOpwBnsG_1DJVVr25#2KhAu|hS08m?HfXqm}yM+ z;Ke*mD+k@FCn_11MXH@$8qv{cUU>ZbaELskbqoxDZ}u$-LQ2cg26;T3kNL zs<@GWPPfh9x2CO8JLGIwE~_&~?%f^m8H3~Z$9ahao91rm*os};{_X|!VShCB%D$9a zfwmVH_&cK1veO?fkb|p#CLf#W8h4DVDjYN;YnKMoj2nmvBsceP$rcO&J?A!f32ImE%1&w&rISDF&S~*@t^qEYVph~ziJ%(yf{Cz zbcYQLmf-wUE(g|o+En3c{++i>iN3kul5|cI@2s7_q-97fpa+hBDcX+4o!$mx9#XR| zP9x+e;8Wat{?gW=JT}%H*?rSC6s90%n(;VL+ke5FM-6`sniprzNaP?H$PG_F;inNi zF2s({3R|o#_i4F7=i$%zmWPrTBax~7T_CN1I4)jpR6Fs*|9!*>@PTDC1 zT(`n}@*f9(={mtESuNsMfSJQRT~I_c0tFmG88{IDoR5xA5ZhA`#P5g?*0K?B=$l1v z(;3oP(tE9DFoUea_5y5P+{?*T78U%;N$i zQ$M#%njk;7nND)qD7S#8Q}RWe>X=G+Ioh2AEauC9QK$=`>(BWmu*{Sb0zZ5sKb#T^ z{ebwrELbPy4L8k~KWe2JJ5jN(;rH9J{<#x`RLm$Oy040;J&ZD{g@JJ&h7-VylNgn7 z^c0_V7C$e5oiH8EOFBlrh4z>A`i~Jr{~C!91Swwv6}*A-C>u*?oN+QBPy&tM-;xo( zPNj)|a*dNl@W}d>W}?fXj`N2Sy~*nz5Vox4r&lSd0^-L^V9wggWR;{t#LVG!o8m&A z>l0tSozDkxy(S}BPz;;{rW7O{%ezCgl?$scOz@opL|^Vlzy-Tr&<_=c3!al0zk#>O z`!V?A*Yy0|Aw^^qRPBjy!g(mDv(c5Fvl8`xVhhnesOVbQ`6XQVtmC*TGQ}ApN4&c^ z`L{SR40D}4FOR+&1v6a8g~_9uY)QE5B^)!=pn1LrZebetfA~hWao#d#=w7IwjUaw5 zF2HKBK9?^WLGMR3^6h8)X7EL^3%B6h-EaZh-9#tBsoepaAD=7;ISBBU6BcJ2zh5SQ zeM|KSO)3X!W3pp9tn&f|8D3!ihr!dP9r zRq4I&Pm+HSIqd61=Ub&t@ncV7!3-bKb7)|G5C7keoc(za+74945pa0#^Y|?1j61`i zqsmSH&HW|uM(Kp%4gSzL8#Vgh;uIHuDvRb{nUd|C1w!ys@TS)ufn6NG1{bp4Z@Ft$ zeR(Eb`I|wKn5>@=An-5aa%2xb#DpX>Prj1XhY0fxsOVed>WTf}%Gbf%XXMhuOHPn) z71sf&0)S~B85b@0(39|%*`iy|SHvj}lJG1+wU_opgI_84)_*Hy4hJmg2*VA$eO|ddsV8$ruN)-f z_)m>tyFzsKo)8@kl}$vha%!?9I=3G+p>5dp=RM}vlQQX@Rr6&2xI$W_2@ zcZ+9rumQ3&X0OEC_&$Rlo$}a!5L0}ggD_qs^g;fL6+AIqKnMV1E>fOj&9C<6aQ=S* z*9abByB~shv_XKG^V8J-l{tfXFfVJKT}&K`SerLw~ImWbl_Hd1Pk~rb`o!QjzcahBiFBgO}1lr_0plif&%w{+(QOe@80+= ztEhXLP7KPxft3BqD?iGdp-!Ag*)P9L>PTMi)(bOP09#7}tcJ|L{*|Px#3;^>LMkYr zj6eN}qzpE+6qK=ml(QMP+(L3;AsOh2hhKl4j5;0+OAn)^g~D&VLB@dtB$t+w>xLU* zza)^sWx!KB#MB#Q$PmhZe$jd(g!h6ANZ-4cA=6R zvl1{4a%j;F&aVAL{>X-f;6MIB?vqX;_X#IZ@X<%fHe&{<<5J%}&xM|Z01HTrnCp9b zFCKEiDv~Y8?CK(aGk{xcHgf&ySFy9*(08PKUm;aM_}$bO)ibyfIWN65cGL~$x#x;X z*%R98ln)6A0SOhc^d1)oILU_r`I@vJxjv!jFjx86$H zmR?I1KI9~EGnWE=_iplDd@)6rCYC}8a7FCNoK@<uT^Sjpg}Bz8|?oToO0%s`Ye+XQx`jy!+?$?s?HjkG73u`lo$x&7lQ6r4qc1({@56jE)n zm>JFB7u9!mg=qJI{j_Cggl13en>I(iPB3##|1Eugyx|Til82Mm>l7*V`a_hRX)SCi zD)vx@J&THp!jzxUj|y^}^ujy4s9$*woiwv1g=fk^ocXJZuA-)TYvRm-mhOqFw(4dY zn`gaTDPu^t&p9e@7M13nD1?j{Y^UiHh9~$YcDNvQ^ITfKZZN&^nYF%TLnGQ+Ajj4{ zf%A%gUu~k{1G>F#$S8&PC?<2;->Iyql-^uaM`xcp>Q<@EmAc6zIS z=|rU5L9@i$BL-0|O{K5CaESX@je8m*BDVvIhS1s{tnyR2a7`LV?q=>3TZpUX>{Fa{ z^|76F^OqB7@fL-K4d5s2@C7@pwa_fAiNAhAD+Oaq2$xrPyDUp`?|_QcG~nb}^rvZR zf_d=>JGD8d(tdrs*gE@9rJGK7O<2myIn4<=m3X+zeN+PGF^o575i)rvYJ81g=E zSx5((8BRp}TE^(=gLM5Ajf!%AoT;v&dekjc-(XN>*0=P;wGRlL?q3m$|C11i&9q`6 zydOX2>$$K`8?=&+J<=*WMnZry<9Fohb526Y)AP?D+m7`@NSq0SQ_Lx6Vi*lp>IL(h z8|b)6#l+<*P6CM?23NXIzrKnj&o|VkLZX; zg~pGpPEj`_<$%%9(r#|6y^ezQ{}w{x__pyX+UDc6Ko-J%2GFd^m*}`G%L@n8SE(@T zq+U&@IB4^P%y38-FGThEuno1*&W0tF9eSR!lyyQ-Wxv$a=awqMy{z1NB!%>cC@V)1 ztz4F~h_bR2%F1w(!^v-d^aJ7onp&*N!U9^_EF0{KY@m?#0~KqQA7Zqs6UeE?x>rD+ z-CEjQQk0usKpY-Fk2)c8(shc2{A7|-+wIE^`Lovo6LXfkX~sDQD)EJ4^=^1z3n; zN$wZ(a*fnSx9so7$8#bC#B|Qf?0Tm>GmGSNe?ijh`EaY~2*%#y6^DLo4H)oXbqaWz z`XmU6#*@N@*_(NPZUHGTeo4|r*ArFrLvnEWzFnv|hcucvxMDC*l975gMRCv*@{NB& z?LmvjQyBhy8|kNyC;igdWW4i2?25zU4(81{U_0+^iqNw($!h7$61BaP+4H)mCLU$q7%+4j{$#+N^o;q~%Q@{e!u4xt6znk=5pKUFV%*iMD?{AZI zhWMKCPlYut+S)$^VE4vrv1J2XlYhJpBxBu=3qY?$zQhXE&96Tqb15&M{mHQAU4yy< zn`B55$u~WJD9V7#!uT6^;D0jMf2@^bjRpJjUXbRVLDCd{gAfY*FR1KX;6EMQK3MDY z`2fWJycJ8<-U4j9xiNMy#!1Psenquw^Y6|Gcmz8NrWfkgU^WNui-AnF2&srWv4m zqm$Nz7t)Hnuanl(rokZv1m03dsj7Aln&9K5%14}11?qzf{Vr1?GvXuDT~B7yhW*;! z1<(3_E)G0aY_0qKX$ZY4pB#OId>~WX@Pc_r7`myq?lmLdKKs4*2ITOk!25TqiM*tA zgJZ{A=%*hV`-{d)eiGHiCMth73obUA;Z!1L)}iJ*w4X zt8;$g=P#hAAVq){@GgL7JC&J7(M5_Punw<(ejEjonALVz$c~ItG)+tKYY71n(;SCh z`5k)87Sh0&-@B-q2NigH`*&=L?1hpS+(CrXYCG7H8*RnUD{h5(78L5(_p z+Ep?RzCzlgw7UFKwL$=R-rg5s;KX+&r{Ax_mb2VCM6n#5F0`yN#urCZ}D7sQ!XI0 zZ6!$`{GH5TKujiPAs{hVIIg0!FQl@ef6(u$+o|&2W#sHXkCaYSZPY?{Cw~?~5o^+f z5T%IBjn5HnT?=O`SpsxjOob4C0(?H-cgRxu7lNVFkw4)m8gTDxRC42M$c2j{?k+UZ zP>~KH5F`u%W@xToj08kc$YvlCj)<4G2?2R|dHd_@>px~QI~3Hc94b5cJnHw*CUOkB zkiu=PFk#dWp(>~h-2tO+Gnu@2QILu3fvd=H_O93WY+#)Z*aE z!`b%x{p8EeC);IzAbI=^qH!U>hZICb4HRa@f46)6qQx^?URhyuS$ zcgxYpI|fV)Rb8Z*iB-pcX2*g!0y2>sBR8LnnHQk0pi;5fO2xdA@grB?DxL(AK#BlH z{)3aRb?n`-%S5d-P{4vA*Gy&}%ZaF|ItbC9#Q|3-nI3)hOTWqC@4hCXmw>8wsWtbs zb56`!`r%r&VLx<>iUT$AmnT?B{C;Z#4UA#n%+~dSH#E&d?HBcbTP)tDn1Vw#`mkA7 z*Eu!UN5`Cbjb1gh#^fG;My4ne#qS|y^BR(uJrFY0(oacJ?yh`uU)s8KNC9!6if8J1 zG})Mh?rv_sN(uQf+v?j9Lu@xHi&ypiPwS625{jD~K>!pn!y>Q6a86*U3Ui{Xik+aSc?;Ywtc22&U znfaipMMo=khp_vMynM*IbJ8k?H3EINKnJ{4!1LA#p!+$k5_(&KG0F)P}z z=(YS^E5H8po^?wKY9`G)(J+mVji|-^?30e6n=YG=a+Jw`mYGS;ygag_f1o-u=wL^b z++GI(G9At06>33OhFN?4(pT?Ty5sA&Q$8Uia7lf$}C$+Y=(^s2Y$ZzD+h(gp4Gbly<;jt~d){T7bKVN+=RHO$zIfaETvco?7 z=yZ419UxyzBL&@^6k5B23Nsy0rVO<>g&>=YKf0ij-IYOEe>Swv4ld83EVPso`iJZmqapuP<+z~( zu2W<tFmWhQ$cAdZI=7d^I!g8c~Nc`V5WhNb_8c31ZK#EE0cm<9po}1 zG`bvdr;Al3A>MM-$cm%*pv`3`6%9Inn=S!aCB+Hfe}nU74oxtMvT8t%%xqSHIF9&y z5HhO5&)<#-X=b-yng=o?D z2f&z93l)L%FiaHsj|Th{@%yL?Mj`n+X?9H}A`2)%L;10wOx2JhUfmRH?_y=^@rEgY zcr-i5e@U~eRPvyF)^6E{c$FE(2*PZ31YQsjg#e_A@^Z7OC;-35e7N6<3ZP^-qp~wI zzk^K>1}p^FV5=^tjk2>)*T*OW}U|0owda>g*U0B)on0+1sk zs!?7BLQ0o4bS*xI;WT0Pd5v;rZeCp!!2}vBe}N3AN*NgrazaRFXMmb^?VyR$9-1ug zp-5XjHSgI^o=!LA5X{u)6=mSe$e_6s27rk~_KYml5o81V`9=MomGjCs?s$$iZrUm8LMCeU-4{=%;n_Zt zedR*5NJ~+nlxzuNrQ&d{hx28 zd*53_b)G2Pg!L7UEade08>UmG+(r>k8x@WiLZPlsv5wG9#iE51Mf>3+51;e>=kNSR zRczF67@`G~2Y*go2*V}f(~8-}w6@8le;*F|$N`I+Gc<>WqT9@)&5})`jH&|4DypE4 zjeAg6Ofzf9QFD)+dwTcF{a^Xr%`fa(zI3=VpC2(gxn-d|v3h}X1Y+Ise-K9(U`ct! zIfUq*Y(*;Cid^&+WC}HPk*#X%!s%yS^#t>cX`blqWlKNUxqjoRowWxu!oCPPe;XS5 zX9!KAh?XNdMM3=GHJ>{|-gX_qEqXLY6d^op^;{UjU_{CXPtjt`f1VcKJ~P{ zdE2&E8&|EY;^lW%YGXepff;7P`{tBpQf6@m<>%*+%|K^SDQdXk~v`mw6SOK5D`DRW>OUvbcpKq$y?H&;h2K&PIf5Vz>+np}g z*IC)w-xgO?d^i8%i+|pL)T%|FW^LcT?cV14`b#6hUSpm6i5An>H~+(J$Z@k8coP zm4Snd^@sU50`XnL$Bw=7f4pD*lIQIukRssxUtVx~BosL#-^?B~rca+7cUv33_7im> zN)9yih2`Z0)sWxgp_cl3O9+JN$dy>C3L(hJtruQ@eed^O-*nIK&OtZzpCtvwnTA(W z%;pvfcXUv8b~cTjK3#M~xF@fHR8^&72-~@KEj8>%hz}te1n@&ve@Ru_C(W2K`Q(c( z5<#)3$(R1-vKm>E7uHnOl-5*L%LnV~Xw=9fNOQYs#FQztcEbi*yLBt=X=8hyb#j z*_4CSFGDJKG&GZ$e_uw$eXGflQ$TecZffrAq|UY$s_NH|3<%*iI*v#rQdQM&bm7W% z-!D?8%%56R-LLxTAa{IGWb7hxa&oA>4eH6sr_a7yLw$yhqA?>!QU88@skEdRuAzup zy#YGdP*0(Nk9O~>r7ko)+^^Ho^MR!)s#6_URaNpYW#(}+f6s?bPmdthbUIvU%`ya4 z^{b|Rdk;`oNT;%jzElI|<)ww!xTayTicr3 z3rcc|WbN{FUo zP>`FU)QlW(i=ryooO-rzMd1$&B04~-s`^&Zif>mSm^I0V2%@RAg_@cgr~{F=yQLmh z)J_*)cp8dQ0@pHeeoz|{`BqbQ72a~W_ql`Y91^wYqafmB#f zNZYq=f1?0m&w!_ky4qT)4)q}D^V8X9oJ7aYK8g-CZjNN7*=k@cq&QQ=?fG7_h5b+|_g9Z(xitZ1}in=-*Xm9Nns8vM*;yGZ4h+rLCSyulc-F(k2xwQvs58`ZgJ3?|U z2Oec{@+&R@hvFg|Lh(M(^HI~rqju*~S62&pJ)IN``H^E8iHRaeh!3Z+{`otOs@pWB ze^JVJUoQ_&oiVj9!gujb0nQ13z%Cqt2Q{5knO95&*%A#NR6?Pkn>yN?5N&vI4dPI! zMMK=F;d-tU*4qVSkt4f3bK#A@duSn>DZ7T>tFpYku%L)?vT{g8jSTyuG`eOeWjcH` zpntw7M6Mr^2x3wYr<%X07h&JUJx-{Uf8QO#8pzDbYlX4!2S_Y$x^l^i(DdVG?A4;$ z$q?pXiwbpxX%M2dl0uuPd|cCc%XLJCwV@#sCds-sOjsT|9d>@bQ+7UFt9<)4zq(sI z*-HdoE0?a^HT(G45^DAYxCdtdDb|Bg%Fl5K^MxB0g~;H2VKr7M+l5J>; zC1^WNt^NJCbcz>}3VnzJ3+u`~b57 zmFfq$gMVIr_3c0J{Y42ZaPoRZ{;~{Du;9`3_^ZHq*qG!0#h~|TK`8CadPaHy3!J>x zWdZ1PQ0nBa-r$ZIzt?mD1f{IcVDuKSz{yJ^9s|Lbv*Y8ZML1#u;;Xp`f4KJ8u#sG% z;faACi`)nyDeLr81uSs#w~554E%$JABS3|63Eg$XZ8Wz37|KMC-iJ1Lb?q9ud6V^+ zMwT-m#4_I)ZiSG<_cgl(EO1_ezujw+j1Y};j;DW~`XmkQoBFYH9*5XiyNNFO;7Z!9 z@%nkoZzbU}2obz20hZ2re>kmsk|Nyodndk4$BmyOYH$j07e7n*eBGBc{}U8b%rPc` z)isIBMED|r3$&X#deZ$fYG`ED2)F{BJV7sY_&U4E+d&?`hsF&ULlurnBAzOzhY0Oq zjDg_AJdqa56!|S;-PQv2%+8!euU`5hWx2BGsZXAvCwBak%5D63f0<}Ucn>{!^q=X( zamUktzI&GnvkU2?EuYW}jW41?yMzh>wytrU1Ge*CrWiI5L~$dIe?cuo2SfX4wQm)z z_N*dzR~rQ)0d^5|&e3Pm2@~eh?elJ-q4r^LBZ)o<^PJBDW{E?2I zp86z>>^lMlX-A4;8HObuIbb4H7575}R6yBK9p{h%4@`R+YUwl2=X@Z}#W0$4(ETL< z^o9RA=25y}#(6Y$;27#tSRn!ck$znH;SV3ti%VXn`nGxze|?D4XeahJAD}xw{i8^| zDi-7?P;6Zl2U0m@5Wh@@zrD*Zqqi!M$N>_2&cw6mhU0%Ja?s`Dk!33aVMF-C?AvI1 z&UEw>(B3401$4g~2TA3?t0zPbpv+%=@*6ty<@0F3v%~4Djf+W%&AH3V%%#c0C($pD zywBXnf$wTK+O-VIn@aS%44wV&@!7Bu)zP&*(=TvX4-$t)*f0I^t zSC9hchz%_^`~ipsq4VMbQk0oOe4Ss&O)kZLw1Gf#S zZo7xGf3sR5Ya$mQs+IzKW-sXkq0;v)>?>@)D7TQ#D@JBn*Zdn_BRi9sGq~Xqty{NE zF44ZH87e!H-rro5G@6z-mHdGKHMTd>_NE=Q!S_8FTZP^O+~6eMhMQ`S46EVcurJ}h zRs7SUK1*eM2Xdr9B}w{buv&f%<|zwld)KNZf7%bo39sbgN9MfhL=o9%6SaYOt{wMY zMBxaqwY&wo5X=+r+>fJo4wBIwwy?)#rW^sM@%cZxT_3tYLR3tSbo7EYp&cOa7Y33 zf8W@YvpksfbVQQQo1df5$b6OZV-xe;s2AJ2H2Mq_){PI2wCPuTe9@q^(`nNRnEulC zF-s$kSASLJ7&&i{Q(V;OHmD(nEET-BZ-)#ValwWPJr9 z3B3eNdSU;CtD?$Nj}LQXjjpiKJ6po^e^?9blhp&7pCeI4hD=X2>U34HLg$Ti(t%Em zF5MUk9jR)+S$%o`lsQHT0l$3pZS}j*w-|KOt_aoGB&tQX=}Vm3*{aiZTOu^prawQ#*>?RK7mg9Fzi<`Pp4?r$f7mzm z^ETaf<480($3iVntq#++kU|4&3Y}DyL8ta{(zSgYG`S{&!sylfx+arq?5Go(O!uw` z)5Lyun&VQbF>1f!dtd3-lb_lzUh66Zv{TlTe#1WVU__>qN>udnC3+2(-Vg2VMlUpj zdgcw8blN~0<$;Mm!aZTws@tS~f7n~nB~w$ELB~}n)P&#shB~R!aPkLiEdc>tnX2)N zACQ{T423)hs7X^014vXR+5!nKTBnKCF0^hYZEi$i8xjIBg+jQ744K-!5*4BFDaxHi zg`$RhTtdJBP5GfgLw0dt8(dB%uhD@pKHkfdn4tf0r7C6EW!iEuLP3}he?P$3AAn1q&5CS%gdSS_p*$t3xr%w)|P+c;ekcVuCWEurwc2%Nm6&Q7_KSLm+7m0Ff-jwzsPK+b)#Ms0)!Qr>uIYuGHuukyB?-r z?-@&fo@mnODwB@=z@&1ge@Sgn$8RbnnmIsIzbH2Qe-2Xxa~jrO*v^u@_C9fedzQHTvF zm*-ZXw8PL1Xm^)HgB6)RK1m^eSfwk!(dqqBi|E%QCQ+VkG2){Vf3apyK;X| zyHn<8ggCRg;~u(Z?bS4)UjdyoQUe2`eXS-ndbm?%y*sD6*r5C@nU91&7rlS<_2lT-MaR8qr%w^x$S}td zWikx|xzJpGvqPoXe{VXdvu+PMR7lf{RHL6Sp+cgfldkmn)^=I_(L#W4;lvteWdHk$(K1rpzyk&q5P(Kgk}M&d zIA54Q8?I26<6U@CPK~8+Y6tT=5TD`!D!FjG-&Qg|@1iZ%yVMx&f6vLy2yA~^(Hjwy zz!bSWiXfy9f4IPKBvx?z>UbhJ2x!1HZCuxpa61kEq|>1u=q&=dSdxC z=eVN&f3Dx7$8Zjc{~2d~+21BV{tDYvFZ^r)OD{fgvY|(wHVtF6RWJ}C%S&aO{gUY~ zeUkFwRK_98ARU%mH1`Hw3oW&n127@(0kwDo(^nj5?_SfLgrAgyHMd+V`x^EYJ{V{Z0Gd>dBn%3PV6=C>plOvR2@YHi-Y$RO%C9$`{>=I6+j4Hm?@#$IAoSk}1qInJAyFxxf=DYs*Mh%+`6WhS*kU}PD1BJl zc9Y-dOL?5j08l|6%+^4JHn#cSeRJzB@l2p6 zOjGA?UaQ<>_@jpPI*1s03DC9hsYtz;GtFr5M>p2`jD`2C+jYqAy(0(HL?Ig{b%diS z-_`6T02iMb+ofPsqYbUzrypqwU8(uKX(vstx%pbfG>sE@AH5yg)b2}pzfg*Re{U{3 zr5qBhl`1H(ra-jrrOnN6bZ*&z$S6Gm%?FQ$0I}q9IW$&J`05!Q&zR43NQ^3RWSSrbeA-C{l_{=kEIu%aJ*q? zCv$F5tLdNH>Uw?jKeY@@j)*Mye>HR8+J?v>JJ`#wKFek3(MLEz{HFAsUBM6bH#hhC zG<(k+B;F?!(aqh#U|s46UlO7L_Z@KCBSi)Dg~AKg>`uEHE=7PO$%zRpD2qeR_CTXE z6{@+Fnft;TEv@VPQ`GNHJLfl5MUtF&OO=bCxTV3jSDc=U7VTN2K#ii;f4RER{qdKb zC~{U)2fE9HGf=rU|u-fcGyKa&|)fH zIel(ro*e!nQ^grzY2w#4!e?z(Mteh@}Pmm z=T{fsoMn?vKxe)Ws{{3-snH+V@XVHdFST}ceG$9#|9?ow9y=p#F8(jUqXPf0AWM%v z`sb|4Q>M+a+wJH9h%VfJ4PmSJgQio8Mx((E>(_rF{rjzVuc)pbfAE}wdd1_?+^)vU z-rDCQ>?BKO{}bEcR;=fihn@N9$iV~qel0!y&lmqVV9=1e+FD!r1R8LD;BPS$JXt7j z8N6e0wl?6F*a||6Xz0|ZPbHOUVv?93>zvqvP{TpiG4lxU>}Is(%u_>nDMLlEe*@&grc=|_Anjb@r^<0Q zDjBIzB%~qIV5SxWo`t{(;p-#e2n7N`@_0Se(b-9DZEduB=N@a8PXvj^HL?b2d|?y^ z*31K)!hY_oK)>j0hul#Lc*5lC4DymDa=W{zqrIJ4VH7PbE!5uLM((aI3I+mV1{;GD z%|e)`$N|qOf8yC4{*C};SDG|lp0zaNzDt=?scuS{O=8v3t>goP}KkjmGpO#Vz+uU z%tkQmL0lFL=E?l5GT;NKh_{T*i#lo3C!LgcgiNdcf7M2B-?X3Jxp_Z*bWa`mJ0mbv zi%~j?KQyv(VDXe$PdwEmu0O?zwva_K z^~hO?OHX7Op-6J}Q_iQO*zXLsHWtE`hl*y+OyiRbj&Vem)1>aO#P{G{hUJT_nQx+^ zacRc|e^{02zbA`Fyw2#vzDU$O&IM!tj$U(8e>RvG*Do<0>z6m&^vCR+yt{ZJQYh$8 zJSUDn2(>os@T_edmi=>~$M!+u402(2xCHtAp1(+!U-^q$3yVq~Ux`z!HS0q(c@W4kW0^ xzjf=TC-@H5i4&AIRQw;nR`a9NrvFC}{eSMY?UA-C6#oDK002ovPDHLkV1k)j1Bw6u From 2c8aaf6cac274aa4efe97be7ff782ee2c3a5f1dc Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sun, 26 Oct 2025 20:09:54 +0100 Subject: [PATCH 15/42] Captcha-based bot rules --- admin.js | 10 ++++++++++ config/default-config.json | 12 ++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/admin.js b/admin.js index 00cecc9..751eb9d 100644 --- a/admin.js +++ b/admin.js @@ -1680,6 +1680,16 @@ BotMon.live = { return (countries.indexOf(visitor.geo) < 0); } return false; + }, + + // Check if visitor never went beyond a captcha + blockedByCaptcha: function(visitor) { + return (visitor._captcha.Y > 0 && visitor._captcha.N === 0); + }, + + // Check if visitor came from a whitelisted IP-address + whitelistedByCaptcha: function(visitor) { + return (visitor._captcha.W > 0); } } }, diff --git a/config/default-config.json b/config/default-config.json index e9e28a3..8134cc5 100644 --- a/config/default-config.json +++ b/config/default-config.json @@ -41,7 +41,7 @@ "id": "susClient", "desc": "Client identifier that is popular with bot networks", "bot": 10 }, - {"func": "matchesClient", "params": ["undici","gohttp","urllib","wget","curl"], + {"func": "matchesClient", "params": ["undici","gohttp","python","wget","curl"], "id": "botClient", "desc": "Client identifier indicates web crawler", "bot": 100 }, @@ -69,9 +69,13 @@ "id": "langMatch", "desc": "Client’s ‘Accept-Language’ header does not match the page language", "bot": 30 }, - {"func": "matchesClient", "params": ["whatsapp","applemsgs","goognblm","tiktok","meta","chatgpt","claude","perplexity"], - "id": "previewClient", "desc": "User-triggered bot load (e.g. preview)", - "bot": -120 + {"func": "blockedByCaptcha", "params": [], + "id": "blockedByCaptcha", "desc": "Visitor was blocked by captcha", + "bot": 20 + }, + {"func": "whitelistedByCaptcha", "params": [], + "id": "whitelistedByCaptcha", "desc": "Visitor uses a whitelisted IP address", + "bot": -30 } ] } \ No newline at end of file From 6b6cd387da613838df43ec1dc8194b7d3a057341 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Mon, 27 Oct 2025 21:07:05 +0100 Subject: [PATCH 16/42] Stats improvements --- .gitignore | 2 +- README.md | 8 +- action.php | 2 +- admin.css | 1 + admin.js | 259 +++++++++++++++++++++++++------------ admin.php | 18 +-- captcha.js | 3 +- conf/default.php | 3 +- conf/metadata.php | 3 +- config/default-config.json | 2 +- config/known-ipranges.json | 24 ++-- img/addr.png | Bin 1974 -> 2170 bytes img/captcha.png | Bin 3382 -> 3075 bytes lang/de/lang.php | 8 +- lang/en/settings.php | 2 + 15 files changed, 221 insertions(+), 114 deletions(-) diff --git a/.gitignore b/.gitignore index cbf6a5e..a377e1b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ logs/*.log.txt logs/*.srv.txt logs/*.tck.txt -config/user-config.json +config/user-*.json php_errors.log diff --git a/README.md b/README.md index 547d435..5c9c822 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ # DokuWiki Bot Monitoring Plugin Plugin for live-monitoring your DokuWiki instance for bot activity -IMPORTANT: This is an experimental plugin to investigate bot traffic. This is not a "install and forget" software, but rather requires that you actively look at and manage the log files. +IMPORTANT: This is an experimental plugin to investigate bot traffic. This is not a "install and forget" software, but rather requires that you actively look at and manage data for this to be useful. -This plugin creates various log files in its own "logs" directory. These files can get quite large, and you should check them actively. +In addition to collecting a lot fo information about bot activity on your server, it now also has a simple Captcha function that you can use to block off bots from downloading your precious content. It is however advisable to only activate this after you already have a better understanding of your own site's traffic patterns (both by bots and by humans) to avoid over-blocking legitimate users. -Also, these files can get quite large and fill up your server. Make sure to manually delete older files from time to time! - -For more information, please see the DokuWiki Plugin page at: https://www.dokuwiki.org/plugin:botmon +For more information, please see the DokuWiki Plugin page at: https://www.dokuwiki.org/plugin:botmon and the documentation found at: https://leib.be/sascha/projects/dokuwiki/botmon/index diff --git a/action.php b/action.php index ea97afa..c4cd017 100644 --- a/action.php +++ b/action.php @@ -251,7 +251,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { echo DOKU_TAB . DOKU_TAB . "cj.async=true;cj.defer=true;cj.type='text/javascript';" . NL; echo DOKU_TAB . DOKU_TAB . "cj.src='".DOKU_BASE."lib/plugins/botmon/captcha.js';" . NL; echo DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(cj);" . NL; - echo DOKU_TAB . "});"; + echo DOKU_TAB . "});" . NL; // add the translated strings for the captcha: echo DOKU_TAB . '$BMLocales = {' . NL; diff --git a/admin.css b/admin.css index af620f0..cd22926 100644 --- a/admin.css +++ b/admin.css @@ -653,6 +653,7 @@ & { display: grid; grid-template-columns: min-content auto; + gap: .25em .5em; border-left: transparent none 0; margin: 0 .5rem .25rem 0; } diff --git a/admin.js b/admin.js index 751eb9d..d49d22a 100644 --- a/admin.js +++ b/admin.js @@ -161,6 +161,7 @@ const BotMon = { var bg = b; var sm = a; + if (a == 0 || b == 0) return '—'; if (a > b) { var bg = a; var sm = b; @@ -291,7 +292,8 @@ BotMon.live = { // shortcut to make code more readable: const model = BotMon.live.data.model; - //const timeout = 60 * 60 * 1000; // session timeout: One hour + // combine Bot networks to one visitor? + const combineNets = (BMSettings.hasOwnProperty('combineNets') ? BMSettings['combineNets'] : true);; if (visitor._type == BM_USERTYPE.KNOWN_BOT) { // known bots match by their bot ID: @@ -304,6 +306,23 @@ BotMon.live = { } } + } else if (combineNets && visitor.hasOwnProperty('_ipRange')) { // combine with other visits from the same range + + let nonRangeVisitor = null; + + for (let i=0; i { - // count visits and page views: - this.data.totalVisits += 1; - this.data.totalPageViews += v._viewCount; - + const captchaStr = v._captcha._str(); + + // count total visits and page views: + data.visits.total += 1; + data.loads.total += v._loadCount; + data.views.total += v._viewCount; + // check for typical bot aspects: let botScore = 0; if (v._type == BM_USERTYPE.KNOWN_BOT) { // known bots - this.data.bots.known += v._viewCount; + data.visits.bots += 1; + data.views.bots += v._viewCount; this.groups.knownBots.push(v); + // captcha counter + if (captchaStr == 'Y') { + data.captcha.bots_blocked += 1; + } else if (captchaStr == 'YN') { + data.captcha.bots_passed += 1; + } else if (captchaStr == 'W') { + data.captcha.bots_whitelisted += 1; + } + } else if (v._type == BM_USERTYPE.KNOWN_USER) { // known users */ - this.data.bots.users += v._viewCount; + data.visits.users += 1; + data.views.users += v._viewCount; this.groups.users.push(v); } else { @@ -627,30 +690,54 @@ BotMon.live = { v._botVal = e.val; if (e.isBot) { // likely bots + v._type = BM_USERTYPE.LIKELY_BOT; - this.data.bots.suspected += v._viewCount; + data.visits.suspected += 1; + data.views.suspected += v._viewCount; this.groups.suspectedBots.push(v); + + // captcha counter + if (captchaStr == 'Y') { + data.captcha.sus_blocked += 1; + } else if (captchaStr == 'YN') { + data.captcha.sus_passed += 1; + } else if (captchaStr == 'W') { + data.captcha.sus_whitelisted += 1; + } + } else { // probably humans + v._type = BM_USERTYPE.PROBABLY_HUMAN; - this.data.bots.human += v._viewCount; + data.visits.humans += 1; + data.views.humans += v._viewCount; + this.groups.humans.push(v); + + // captcha counter + if (captchaStr == 'Y') { + data.captcha.humans_blocked += 1; + } else if (captchaStr == 'YN') { + data.captcha.humans_passed += 1; + } } } // perform actions depending on the visitor type: if (v._type == BM_USERTYPE.KNOWN_BOT ) { /* known bots only */ + // no specific actions here. + } else if (v._type == BM_USERTYPE.LIKELY_BOT) { /* probable bots only */ // add bot views to IP range information: me.addToIpRanges(v); - } else { /* humans only */ + } else { /* registered users and probable humans */ // add browser and platform statistics: me.addBrowserPlatform(v); - // add + // add to referrer and pages lists: v._pageViews.forEach( pv => { me.addToRefererList(pv._ref); me.addToPagesList(pv.pg); @@ -1064,7 +1151,6 @@ BotMon.live = { } else { // no known IP range, let's collect necessary information: - // collect basic IP address info: if (ipType == BM_IPVERSION.IPv6) { ipSeg = ipAddr.split(':'); @@ -1604,14 +1690,7 @@ BotMon.live = { fromKnownBotIP: function(visitor) { //console.info('fromKnownBotIP()', visitor.ip); - const ipInfo = BotMon.live.data.ipRanges.match(visitor.ip); - - if (ipInfo) { - visitor._ipInKnownBotRange = true; - visitor._ipRange = ipInfo; - } - - return (ipInfo !== null); + return visitor.hasOwnProperty('_ipRange'); }, // is the page language mentioned in the client's accepted languages? @@ -1910,47 +1989,48 @@ BotMon.live = { */ make: function() { - const data = BotMon.live.data.analytics.data; - const maxItemsPerList = 5; // how many list items to show? + const useCaptcha = BMSettings.useCaptcha || false; const kNoData = '–'; // shown when data is missing + const kSeparator = ' / '; - // shortcut for neater code: + // shortcuts for neater code: const makeElement = BotMon.t._makeElement; + const data = BotMon.live.data.analytics.data; const botsVsHumans = document.getElementById('botmon__today__botsvshumans'); if (botsVsHumans) { - botsVsHumans.appendChild(makeElement('dt', {}, "Page views")); + botsVsHumans.appendChild(makeElement('dt', {}, "Bot statistics")); - for (let i = 0; i <= 5; i++) { + for (let i = 0; i <= ( useCaptcha ? 5 : 3 ); i++) { const dd = makeElement('dd'); let title = ''; let value = ''; switch(i) { case 0: - title = "Known bots:"; - value = data.bots.known || kNoData; + title = "Total (loads / views / visits):"; + value = (data.loads.total || kNoData) + kSeparator + (data.views.total || kNoData) + kSeparator + (data.visits.total || kNoData); break; case 1: - title = "Suspected bots:"; - value = data.bots.suspected || kNoData; + title = "Known bots (views / visits):"; + value = (data.views.bots || kNoData) + kSeparator + (data.visits.bots || kNoData); break; case 2: - title = "Probably humans:"; - value = data.bots.human || kNoData; + title = "Suspected bots (views / visits):"; + value = (data.visits.suspected || kNoData) + kSeparator + (data.views.suspected || kNoData) break; case 3: - title = "Registered users:"; - value = data.bots.users || kNoData; + title = "Bots-humans ratio (views / visits):"; + value = BotMon.t._getRatio(data.views.suspected + data.views.bots, data.views.users + data.views.humans, 100) + kSeparator + BotMon.t._getRatio(data.visits.suspected + data.visits.bots, data.visits.users + data.visits.humans, 100); break; case 4: - title = "Total:"; - value = data.totalPageViews || kNoData; + title = "Known bots blocked / passed / whitelisted:"; + value = data.captcha.bots_blocked + kSeparator + data.captcha.bots_passed + kSeparator + data.captcha.bots_whitelisted; break; case 5: - title = "Bots-humans ratio:"; - value = BotMon.t._getRatio(data.bots.suspected + data.bots.known, data.bots.users + data.bots.human, 100); + title = "Suspected bots blocked / passed / whitelisted:"; + value = data.captcha.sus_blocked + kSeparator + data.captcha.sus_passed + kSeparator + data.captcha.sus_whitelisted; break; default: console.warn(`Unknown list type ${i}.`); @@ -2007,7 +2087,7 @@ BotMon.live = { const wmoverview = document.getElementById('botmon__today__wm_overview'); if (wmoverview) { - const humanVisits = BotMon.live.data.analytics.groups.users.length + BotMon.live.data.analytics.groups.humans.length; + const humanVisits = data.views.total; const bounceRate = Math.round(100 * (BotMon.live.data.analytics.getBounceCount('users') + BotMon.live.data.analytics.getBounceCount('humans')) / humanVisits); wmoverview.appendChild(makeElement('dt', {}, "Humans’ metrics")); @@ -2017,20 +2097,20 @@ BotMon.live = { let value = ''; switch(i) { case 0: - title = "Registered users’ page views:"; - value = data.bots.users || kNoData; + title = "Registered users (views / visits):"; + value = (data.views.users || kNoData) + kSeparator + (data.visits.users || kNoData); break; case 1: - title = "“Probably humans” page views:"; - value = data.bots.human || kNoData; + title = "Probably humans (views / visits):"; + value = (data.views.humans || kNoData) + kSeparator + (data.visits.humans || kNoData); break; case 2: title = "Total human page views:"; - value = (data.bots.users + data.bots.human) || kNoData; + value = (data.views.users + data.views.humans) || kNoData; break; case 3: title = "Total human visits:"; - value = humanVisits || kNoData; + value = data.views.total || kNoData; break; case 4: title = "Humans’ bounce rate:"; @@ -2292,6 +2372,10 @@ BotMon.live = { const sumClass = ( !data._seenBy || data._seenBy.indexOf(BM_LOGTYPE.SERVER) < 0 ? 'noServer' : 'hasServer'); + // combine with other networks? + const combineNets = (BMSettings.hasOwnProperty('combineNets') ? BMSettings['combineNets'] : true) + && data.hasOwnProperty('_ipRange'); + const li = make('li'); // root list item const details = make('details'); const summary = make('summary', { @@ -2301,17 +2385,17 @@ BotMon.live = { const span1 = make('span'); /* left-hand group */ - if (data._type !== BM_USERTYPE.KNOWN_BOT) { /* No platform/client for bots */ - span1.appendChild(make('span', { /* Platform */ + /*if (data._type !== BM_USERTYPE.KNOWN_BOT) { // No platform/client for bots // disabled because no longer relevant + span1.appendChild(make('span', { // Platform 'class': 'icon_only platform pf_' + (data._platform ? data._platform.id : 'unknown'), 'title': "Platform: " + platformName }, platformName)); - span1.appendChild(make('span', { /* Client */ + span1.appendChild(make('span', { // Client 'class': 'icon_only client client cl_' + (data._client ? data._client.id : 'unknown'), 'title': "Client: " + clientName }, clientName)); - } + }*/ // identifier: if (data._type == BM_USERTYPE.KNOWN_BOT) { /* Bot only */ @@ -2331,16 +2415,22 @@ BotMon.live = { } else { /* others */ - - span1.appendChild(make('span', { // IP-Address - 'class': 'has_icon ipaddr ip' + ipType, - 'title': "IP-Address: " + data.ip - }, data.ip)); + if (combineNets) { - /*span1.appendChild(make('span', { // Internal ID - 'class': 'has_icon session typ_' + data.typ, - 'title': "ID: " + data.id - }, data.id));*/ + const ispName = BotMon.live.data.ipRanges.getOwner( data._ipRange.g ) || data._ipRange.g; + + span1.appendChild(make('span', { // IP-Address + 'class': 'has_icon ipaddr ipnet', + 'title': "IP-Range: " + data._ipRange.g + }, ispName)); + + } else { + + span1.appendChild(make('span', { // IP-Address + 'class': 'has_icon ipaddr ip' + ipType, + 'title': "IP-Address: " + data.ip + }, data.ip)); + } } span1.appendChild(make('span', { /* page views */ @@ -2362,6 +2452,7 @@ BotMon.live = { const span2 = make('span'); /* right-hand group */ // country flag: + if (!combineNets) { // not for combined networks if (data.geo && data.geo !== 'ZZ') { span2.appendChild(make('span', { 'class': 'icon_only country ctry_' + data.geo.toLowerCase(), @@ -2369,21 +2460,22 @@ BotMon.live = { 'title': "Country: " + ( data._country || "Unknown") }, ( data._country || "Unknown") )); } + } - span2.appendChild(make('span', { // seen-by icon: - 'class': 'icon_only seenby sb_' + data._seenBy.join(''), - 'title': "Seen by: " + data._seenBy.join('+') - }, data._seenBy.join(', '))); + span2.appendChild(make('span', { // seen-by icon: + 'class': 'icon_only seenby sb_' + data._seenBy.join(''), + 'title': "Seen by: " + data._seenBy.join('+') + }, data._seenBy.join(', '))); - // captcha status: - const cCode = ( data._captcha ? data._captcha._str() : ''); - if (cCode !== '') { - const cTitle = model._makeCaptchaTitle(data._captcha) - span2.appendChild(make('span', { // captcha status - 'class': 'icon_only captcha cap_' + cCode, - 'title': "Captcha-status: " + cTitle - }, cTitle)); - } + // captcha status: + const cCode = ( data._captcha ? data._captcha._str() : ''); + if (cCode !== '') { + const cTitle = model._makeCaptchaTitle(data._captcha) + span2.appendChild(make('span', { // captcha status + 'class': 'icon_only captcha cap_' + cCode, + 'title': "Captcha-status: " + cTitle + }, cTitle)); + } summary.appendChild(span2); @@ -2569,6 +2661,13 @@ BotMon.live = { dl.appendChild(evalDd); } } + + // for debugging only. Disable on production: + dl.appendChild(make('dt', {}, "Debug info:")); + const dbgDd = make('dd', {'class': 'debug'}); + dbgDd.innerHTML = '
    ' + JSON.stringify(data, null, 4) + '
    '; + dl.appendChild(dbgDd); + // return the element to add to the UI: return dl; }, diff --git a/admin.php b/admin.php index 7a3c3a0..e9eda49 100644 --- a/admin.php +++ b/admin.php @@ -43,7 +43,7 @@ class admin_plugin_botmon extends AdminPlugin { $pluginPath = $conf['basedir'] . 'lib/plugins/' . $this->getPluginName(); /* Plugin Headline */ - echo '
    + echo NL . '

    Bot Monitoring Plugin

    '; } diff --git a/captcha.js b/captcha.js index f95ddcd..9f32475 100644 --- a/captcha.js +++ b/captcha.js @@ -178,7 +178,8 @@ const $BMCaptcha = { const hash = $BMCaptcha.digest.hash(dat.join('|')); // set the cookie: - document.cookie = "DWConfirm=" + hash + ';path=/;'; + document.cookie = "DWConfirm=" + encodeURIComponent(hash) + ';path=/;hostOnly;session;sameSite=strict;' + + (document.location.protocol === 'https:' ? 'secure;' : ''); } catch (err) { console.error(err); diff --git a/conf/default.php b/conf/default.php index 45cf90c..4ae29b2 100644 --- a/conf/default.php +++ b/conf/default.php @@ -5,7 +5,8 @@ * @author Sascha Leib */ -$conf['showday'] = 'yesterday'; +$conf['showday'] = 'today'; +$conf['combineNets'] = true; $conf['geoiplib'] = 'disabled'; $conf['useCaptcha'] = 'disabled'; $conf['captchaSeed'] = 'c53bc5f94929451987efa6c768d8856b'; diff --git a/conf/metadata.php b/conf/metadata.php index 64e2a86..de95acc 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -8,10 +8,11 @@ $meta['showday'] = array('multichoice', '_choices' => array ('yesterday', 'today')); +$meta['combineNets'] = array('onoff'); + $meta['geoiplib'] = array('multichoice', '_choices' => array ('disabled', 'phpgeoip')); -//$meta['useCaptcha'] = array('onoff'); $meta['useCaptcha'] = array('multichoice', '_choices' => array ('disabled', 'loremipsum', 'dada')); $meta['captchaSeed'] = array('string'); diff --git a/config/default-config.json b/config/default-config.json index 8134cc5..e7aab9e 100644 --- a/config/default-config.json +++ b/config/default-config.json @@ -70,7 +70,7 @@ "bot": 30 }, {"func": "blockedByCaptcha", "params": [], - "id": "blockedByCaptcha", "desc": "Visitor was blocked by captcha", + "id": "blockedByCaptcha", "desc": "Visitor did not solve the captcha", "bot": 20 }, {"func": "whitelistedByCaptcha", "params": [], diff --git a/config/known-ipranges.json b/config/known-ipranges.json index 68dcf4f..9d2956b 100644 --- a/config/known-ipranges.json +++ b/config/known-ipranges.json @@ -1,23 +1,23 @@ { "groups": [ - {"id": "alibaba", "name": "Alibaba"}, - {"id": "amazon", "name": "Amazon"}, + {"id": "alibaba", "name": "Alibaba Network"}, + {"id": "amazon", "name": "Amazon Data Centres"}, {"id": "bezeq", "name": "Bezeq Int."}, {"id": "brasilnet", "name": "BrasilNet"}, - {"id": "charter", "name": "Charter Inc."}, - {"id": "chinanet", "name": "Chinanet"}, - {"id": "cloudflare", "name": "Cloudflare Inc."}, - {"id": "cnisp", "name": "China ISP"}, + {"id": "charter", "name": "Charter Inc. Range"}, + {"id": "chinanet", "name": "ChinaNet"}, + {"id": "cloudflare", "name": "Cloudflare Network"}, + {"id": "cnisp", "name": "China ISP Range"}, {"id": "cnmob", "name": "China Mobile"}, - {"id": "google", "name": "Google LLC"}, + {"id": "google", "name": "Google LLC Network"}, {"id": "hetzner", "name": "Hetzner US"}, - {"id": "huawei", "name": "Huawei"}, + {"id": "huawei", "name": "Huawei Network"}, {"id": "misc_sa", "name": "Misc. SA ISPs"}, - {"id": "tencent", "name": "Tencent"}, + {"id": "tencent", "name": "Tencent Network"}, {"id": "unicom", "name": "China Unicom"}, {"id": "vnpt", "name": "Vietnam Telecom"}, - {"id": "vdsina", "name": "VDSina NL"}, - {"id": "zenlayer", "name": "Zenlayer"} + {"id": "vdsina", "name": "VDSina Network"}, + {"id": "zenlayer", "name": "Zenlayer Network"} ], "ranges": [ {"from": "1.92.0.0", "to": "1.95.255.254", "m": 14, "g": "huawei"}, @@ -56,6 +56,7 @@ {"from": "111.119.192.0", "to": "111.119.255.254", "m": 18, "g": "huawei"}, {"from": "113.160.0.0", "to": "113.191.255.254", "m": 11, "g": "vnpt"}, {"from": "114.208.0.0", "to": "114.223.255.254", "m": 12, "g": "unicom"}, + {"from": "114.119.0.0", "to": "114.119.255.254", "m": 11, "g": "huawei"}, {"from": "114.224.0.0", "to": "114.255.255.254", "m": 11, "g": "unicom"}, {"from": "119.8.0.0", "to": "119.8.255.254", "m": 16, "g": "huawei"}, {"from": "119.13.0.0", "to": "119.13.255.254", "m": 16, "g": "huawei"}, @@ -64,6 +65,7 @@ {"from": "122.9.0.0", "to": "122.9.255.254", "m": 16, "g": "huawei"}, {"from": "123.16.0.0", "to": "123.31.255.254", "m": 12, "g": "vnpt"}, {"from": "124.243.128.0", "to": "124.243.191.254", "m": 18, "g": "huawei"}, + {"from": "136.107.0.0", "to": "136.125.255.254", "m": "+", "g": "google"}, {"from": "138.59.0.0", "to": "138.59.225.254", "m": 16, "g": "misc_sa"}, {"from": "138.121.0.0", "to": "138.121.225.254", "m": 16, "g": "misc_sa"}, {"from": "142.147.128.0", "to": "1142.147.255.254", "m": 17, "g": "w2obj"}, diff --git a/img/addr.png b/img/addr.png index 417982df3c6dc95760d22415e442e2642723b638..6ced1499c5b939f3db5b268caad2d60fff11ff8b 100644 GIT binary patch delta 1899 zcmV-x2bB1>5Bd<0$q5rO0s!a(000w_(qDgVDh~GIbo}oC00${aL_t(&L+zP&tQ18Y z$M1oFy+u^)Mvc8I8nH!GVxqCc2=*35eX+*&gg~sQU_&txjlH3@^0B zk_aLSD%kMz*`0ar?c2NC$K8wn_)R`Hvoo{v+v(-nDj5yGVD$Mo?XW=-(0zRcx88p? z_R?|bW2Mq*fXoNsyqZ2Wtw9)hT~F4)O*m)4*Knm_OP~?39*oDp*#<1-PtY>KokQfr z;P00B$l*8|ht zUxDzg`jF~V$Hu8z!rmNY^m3f%g2|mOUyvFZt;Ku7dgPM@- zxjVd02?oV|QS3z@6F3or>*9C&_$F)#aisqY-;(CX^f9QTeMf$OC;WZ<4o(dudDa>S zcCnYiQS3Bs3FUp5NhSy2rs{uaUx(jU;JoBHr!oUgeF!JR$mFR~xhlBo3aB}kl{H|s z+zAf6hhSU2d|B85zoq>N3RTuXs*ZNo4eNLgI0IY<_e0h6kX2a&`;z&uxb|_s+Du)b zc7<4Ng(_=cHcSS;*tW2rUjr|}*03V1!$;WTs+8Tn*V7E@|H$A^)zN>x3BQ}ckmNb- z)@P*Nf7QJAF+ylFSBi5=p2kObL zdU@)tR-B8T<##`D)8q(lMtj1h@Ej%DKN=XuuLG!+8is*My%X;q13%&SBUlNn&shaH z(X9r`lDd=@rTYLpZ9acid3E~VX$i_GoiX1^9~jMJCcGW|b=6EaEv3dKNJr-l;Q80a zQ?ZK9r6kU@HPuE2jm`u+!*;2+i{6Flskc}f=-g`egf!5ov0Og;r`|4d68t9h7OPAf z4CeO{$nQ9ux{sg@UPvE{)i&V5_nZYUg`v>Tz!qo`OoGw9R~UbEy4N_zLD{LhjLd6W zSj7^!&GXAWxH%8450`*)anCP@_Nt>HyU@>vJKzg&+Hv!%7HJ^%qT#cy-DV=sRU?+L zE}eiho8vgOD5sEpvgwGw5huBjDm9XMp}&TM@d}xJjN<~MCYm{Md%*+Xe0B>YA6|B< zI20^ZESd7is_uUZu9+SMoHI}x`%&RgzP_Xagr1KvfrTkZ*s)qwTok<&p^??TF3lGA;zJtFvPEh|BiB{K67c19_eGSX{=SF|dUjugfu7wLVc@^r7x4jg z_eHcp{e4kz3q{?nh2y=1N7ab(&LZ+OnRqTJFEU;RM#3-f1q^|up|MclY=GGR%>8kY$Z-#yT{ zsLqj0g!8xaM>C__cPE9s5+#`t;H8q6s>}MEQ(U*bkaDvn`c=`IZXQ!Zo~d{k3)!M( zaXK#)apQ7nvs@b!*(+veBJwPPd*DRq8_;iRtglw6*07pAOE0; zTaAB4ig$H!d*4W1E=O7}OK@}W=jC$uHu3J1rWpXf>W^mW+J+`|xnPylnjYnbiTyWJ}8gNZ@ z^Q!(1uGQl~BGQk>B#$KCJ0Sg@OtWc{Dn&7mWJBDG#NXrb{<(}u-cT{kDnTJC9%4SGN^~u5;q0)MMB$T6#M~2nPoAJX{+lzsBW*|rPXob l`8cBDsXw8!w{%gZ@(%!DX=qPyI{^Ry002ovPDHLkV1jpiy6XS{ delta 1702 zcmV;X23h&~5VjAH$q6hn0s!a(001nJ(qDh-)TK)nRd~_>00w1AL_t(&L+zS*tejIA zhv&A`T2wS4Y73&aE>wxDmevvp2_jWnZP9A4eY-BAYOAXR<%-@)QHl~v>0d+z)u6Vv zXe&foY5GU2N-b?;d!BC@Gt+P8`=&FKM$%vMJKJ~Wyk~jOd){;IZORp39HKQ3JvrQBVc$nx)xCGv9*#_VixI3JHfU7Ln z%pk&6QM`hWol&|f(8-ZR3W(dvb&2AJVUvJVY!Z-)h6^a%$(Iss&<0Du2sY~^6NMvq z4lW9lHNiHfhRK2v2u5LkFx)#Rs2+dc!I|{_pD-H@iRGpxZ;jTd|taszWHd_@iAjl@eZ#%$UOW2L!JH6YKluOGr+hRbLeJ~y_Eny!h z&V!}jEXyN{fVpr+30Fa}7?yssEDsmi#VDse9-SVc)nX8IBx@9)2Aywd12BKZDZ0%B zTw_U{U|Y5s?A%8Goj;Hb8 ziPdp?ZKEDgN5FX65|LHFayT8@W7k4GZ-?)~gW-0t7oAY!icsjjSHgc>=Kt&{9ipve zU&7w-q#z%v_2@A9AF5k}Vw?d_i|886gcD&AHeA5Cj@RMzfT0#GAw4hnR)BH>o`#1* zcdS!$HLL_x_0BL^jMx^vNq7R(X>teGf}`Pn@GVAkVk}@7p&O{47>0mpU4!JHfeQ)0 zhTB8ub6Em4bUQ)eNIic_lj?m5y=}gnn>FX}a0FQpTFg(v47+(;$EQ)cB5G07Oh_L= zxH}(&-hW*@5l_)Ij6_RY7ELseb|*Lt_6w8i;R`q?OeRACc~`p;P(a#Zd3^Q^lj}hv z_(PaXMwT@gLii#q^*A)$Ls^5j!)!8|cDZq$R&Xpl3vOXy9k72EPJ?5*c5fc#GtO|B z)pTcpZS{pc9D#10J@?|K9ohrl426e#>2au5oeK*O`ql6W_$}1z==_Sc3W&96jqs(R1Jgs1x=Ztl1XX%SH5BH_?M%&GB(@6xQ!`hkC*mw^`g1^85Ezle^cTG=GjW05N&bp-UhqF=;FD*Ht=&{)3+0gd&G5U_Fm zB05mfFJcWU`$bWlh}PCt4-$PQ_eqifkIUQJ+uJn{iAYmZAE-QEOB)xKT?c(@DvLA$ zL!;Q&6yHJ%X_?R?EQ_=gEk0NlG7UxQ%=BrFJ&1qGzO;yj@8peZDwu}HiQ;$Ya+t{U zd7336Z3KF2Zl-eZ+aqBz8L1nMQFJ3Z1^Q0p{a5(%^%ug9|8K72De@&<^k#@fLk8-(i0g>V_2UufmWaZ;Sj_nH=N&jk w=K8S!|19Od?06X|g=Kz=Z}cK#{ao%}^~w_bPhZyR0RR9107*qoM6N<$f>ik|Z~y=R diff --git a/img/captcha.png b/img/captcha.png index 842decfa21265918eda71867c9c93a8fabd7b496..50f492bb08e5fe1f297aa13f955f9b3864632ea3 100644 GIT binary patch delta 2818 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 Uomq#wmjD0&07*qoM6N<$g3uyem;e9( delta 3128 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 St?}jn0000 Date: Tue, 28 Oct 2025 20:41:41 +0100 Subject: [PATCH 17/42] Minor fix --- admin.js | 4 ++-- config/known-ipranges.json | 2 ++ plugin.info.txt | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/admin.js b/admin.js index d49d22a..a75a3c2 100644 --- a/admin.js +++ b/admin.js @@ -2663,10 +2663,10 @@ BotMon.live = { } // for debugging only. Disable on production: - dl.appendChild(make('dt', {}, "Debug info:")); + /*dl.appendChild(make('dt', {}, "Debug info:")); const dbgDd = make('dd', {'class': 'debug'}); dbgDd.innerHTML = '
    ' + JSON.stringify(data, null, 4) + '
    '; - dl.appendChild(dbgDd); + dl.appendChild(dbgDd);*/ // return the element to add to the UI: return dl; diff --git a/config/known-ipranges.json b/config/known-ipranges.json index 9d2956b..ff4b503 100644 --- a/config/known-ipranges.json +++ b/config/known-ipranges.json @@ -35,6 +35,7 @@ {"from": "39.64.0.0", "to": "39.95.255.254", "g": "cnmob"}, {"from": "43.132.0.0", "to": "43.132.255.254", "m": 16, "g": "tencent"}, {"from": "43.133.0.0", "to": "43.133.255.254", "m": 16, "g": "tencent"}, + {"from": "43.173.128.0", "to": "43.191.255.255", "m": 10, "g": "tencent"}, {"from": "44.192.0.0", "to": "44.255.255.254", "m": 16, "g": "tencent"}, {"from": "45.0.0.0", "to": "45.255.255.254", "m": 10, "g": "amazon"}, {"from": "46.250.160.0", "to": "46.250.191.254", "m": 19, "g": "huawei"}, @@ -59,6 +60,7 @@ {"from": "114.119.0.0", "to": "114.119.255.254", "m": 11, "g": "huawei"}, {"from": "114.224.0.0", "to": "114.255.255.254", "m": 11, "g": "unicom"}, {"from": "119.8.0.0", "to": "119.8.255.254", "m": 16, "g": "huawei"}, + {"from": "119.12.160.0", "to": "119.12.175.255", "m": 20, "g": "huawei"}, {"from": "119.13.0.0", "to": "119.13.255.254", "m": 16, "g": "huawei"}, {"from": "121.91.168.0", "to": "121.91.175.254", "m": 21, "g": "huawei"}, {"from": "122.8.0.0", "to": "122.8.255.254", "g": "cnisp"}, diff --git a/plugin.info.txt b/plugin.info.txt index 255006e..2348985 100644 --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base botmon author Sascha Leib email ad@hominem.com -date 2025-10-26 +date 2025-10-28 name Bot Monitoring desc A tool for monitoring and analysing bot traffic to your wiki (under development) url https://www.dokuwiki.org/plugin:botmon From 6064c7d5428b252136bc1dca67daeac6d0210295 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Wed, 29 Oct 2025 21:36:22 +0100 Subject: [PATCH 18/42] Updated captcha state icons --- admin.css | 1 + img/captcha.png | Bin 3075 -> 3779 bytes 2 files changed, 1 insertion(+) diff --git a/admin.css b/admin.css index cd22926..5eb87dc 100644 --- a/admin.css +++ b/admin.css @@ -122,6 +122,7 @@ &.cap_W::before { background-position-y: -60px } &.cap_H::before { background-position-y: -80px } &.cap_X::before { background-position-y: -100px } + &.cap_YH::before { background-position-y: -120px } /* Country flags */ /* Note: flag images and CSS adapted from: https://github.com/lafeber/world-flags-sprite/ */ diff --git a/img/captcha.png b/img/captcha.png index 50f492bb08e5fe1f297aa13f955f9b3864632ea3..c6a8dadb7f980d91a023fe80137c2d1a8fd22053 100644 GIT binary patch delta 3542 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 Q?EnA(07*qoM6N<$f@{x{p8x;= delta 2832 zcmV+r3-9#99fKH<7YdLF1^@s65|pb=ks*iy0+Fg83llN|0O$h%0249-kmx4-*fFHC}11c_w2GgRlh(@DCT;qnKi2D1@Idkru{k-?y z5I^*v{O`=2cjkF!pJ$#q_Ztm=z^eD1wFkipxF#fwM(4mQH+^XBzq5ba(WwBz4<&dL zd^>!CX$gJ-&x4ns=+7ag0gVa}yg$KesPKoF2L1^@okd40;RCYh=&$f%cpi#=9#RsN z6d?Gy1a^bnv72e&w{RA&N5Jn)Am5`2-U7$axcCpe5}rf8?}QXkD4@`P7J_55{{nsp z?d!`aaCb>Ud18_T;{%Zn)_9A!*d;xqI{4TtT z-+xWGk>4*Rd@;0~y%1%UTCjuQV7QWkp9zV8^oJ9C1wJPv%;64r6nSlF0C`U&aGZ9M zzTC9f&t0&9!X3bq;47hjm01cu1v}BHfEqbX?HX|gwb}%)BL40yX>!!0Q^c~)u7FfQoDIwJumpsg_p{LMxdA$ZT*WDW4$8wH6%!!n7U00mQmZd&!o5u-V48q@UUg1$jijmH@4s=_(%9)c!~)r5bB*?*&Txr_p}1BU{85^RMphpyyfDD-8*8p!)H0=Ev!da2fP` z@FbhSPsZBu`-2EPa2zvdk7vmV{O%Mkp;3WAh-Zbnr3Z?CaHW4Vi;mt4ACpBxMc#n$ zMgyQ#fk4Ub1WvzqLZ@ho+o31k286pl8qujBB3I;qzbhn+MrXlmx&%iIQgGAq%^4zssrh(n{MR+!fv^oL`1qi=_AlNdCfGsF_wUKnI@Mv5t6tb$DT^Bqy zx91E;=nxE5@D1p|ADz>OaqTntD>?eep>`qy^GEegu?L?{| z*gXx1+pnioY(YG+N?UV+LRP9E+C?cmU!P@!8lMJ#m*hOnH1KTrrz~1Rqg_;30tvPu ztafXO;J2R2N9POgiU*z86tz|s|vNK~c*0T1F>>xkV zB;a@ie-ZiW{vrxh{Y5{9+Mt^$P`b4gn2fzAoB*|1pKel)t&|BiR;nPnbJXsY=vp$( z9iy4BSOEphDV>a!b0mB}v=?^rd#gLguGm%)uBl@8ID6!sqt5x@~w8j6OXvwZCY{ zEhlanVvvGmG`q=P)Qzl6r@v??)Gl&=T6+1O7H@{Sb=z`_og3ZR@Hf!@l||+j z=!Luu{uJs4TEp~p4+}_JAlx;zZOpiuj9O^=tJgNr)9oYYq}@5}|NEfCr^-ghmq1p)4o&6tdxqM9t`^(?4=`LgRR z5RDD>i#AZZY-`B&ASZ(E9nH2zA*DSEm9kllqIS z$VmuqG+o*ih-|NUBXmHk7jKxRZzBnizefxP&>4Y zZs7yp(G;FF`?`{n{+bWAi#%T+1)l=1NASJ_go~)4(_f@*WBf(XP0VvZS8=!S0d`UO z9c^az`-D-6=6m0OcE6+Lg^G7HYP45HNxN|KChurzp0ov`{*IO@c|L5bxoCYytDgsx zu}!@YW?q+>+4P~hj>%Ym7lS*oU3e<_wW$hJTOjloX>4g#T!Da>n%IcR*mtxqK!sbT z<}YeH(Hh^;s9kCo)!xzgefT>XLTtg{cQk}II!4+Rh|DhPeMdw1g6$#|)!xy%1V=Ot zat*8Y*l$m`FWfEXQq%IEJJH#KRoRCdzLgyh9g`IH`Q`BRP4oDFzUXiQh3ZM^dq>kQ z@@oaZ;`wGsNl;RNU|+m7#Z%AkH1KwG9E$wrBA`%!@Ty(35ES1RrGs49GK+vlyC^Q4 z#egn|fK)+smI!@BABJ8G{Q}CvM;h0O{t;-NDhj!&(3t3y(^|fWe!MlL)S=h60OR*m z0x#k!^=zo^NoE>jSyG0ky~rDl4nV&tExwd(1NoKVqmgUH z56jZ83md!0!{R8|D_rDxG&L<#95fL{3#f_ai8p*_t%+}k#3_LcKO)ewaLt5sC2fHZ zZ>wPSqA?9DtBm3iui8a6cetmkj`s5TU@Zz3Q&un1L3RP{vWsHbU3O6!U1drOjf}hO iqFr{;|D9bl8vPGwn+Ba(hr5>m0000 Date: Wed, 29 Oct 2025 21:51:18 +0100 Subject: [PATCH 19/42] Updated date --- plugin.info.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.info.txt b/plugin.info.txt index 2348985..cfb04e4 100644 --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base botmon author Sascha Leib email ad@hominem.com -date 2025-10-28 +date 2025-10-29 name Bot Monitoring desc A tool for monitoring and analysing bot traffic to your wiki (under development) url https://www.dokuwiki.org/plugin:botmon From 68ccd2abf9cbf5119f38e00b049cbf507612014d Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Thu, 30 Oct 2025 19:15:41 +0100 Subject: [PATCH 20/42] Small updates --- admin.css | 1 + admin.js | 12 ++++++++++-- config/known-ipranges.json | 2 ++ img/captcha.png | Bin 3779 -> 4104 bytes 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/admin.css b/admin.css index 5eb87dc..c50d2ee 100644 --- a/admin.css +++ b/admin.css @@ -123,6 +123,7 @@ &.cap_H::before { background-position-y: -80px } &.cap_X::before { background-position-y: -100px } &.cap_YH::before { background-position-y: -120px } + &.cap_YNH::before { background-position-y: -140px } /* Country flags */ /* Note: flag images and CSS adapted from: https://github.com/lafeber/world-flags-sprite/ */ diff --git a/admin.js b/admin.js index a75a3c2..608225c 100644 --- a/admin.js +++ b/admin.js @@ -730,7 +730,11 @@ BotMon.live = { } else if (v._type == BM_USERTYPE.LIKELY_BOT) { /* probable bots only */ // add bot views to IP range information: - me.addToIpRanges(v); + if (v.ip) { + me.addToIpRanges(v); + } else { + console.log(v); + } } else { /* registered users and probable humans */ @@ -1864,8 +1868,12 @@ BotMon.live = { } logtxt.split('\n').forEach((line) => { - if (line.trim() === '') return; // skip empty lines + + const line2 = line.replaceAll(new RegExp('[\x00-\x1F]','g'), "\u{FFFD}").trim(); + if (line2 === '') return; // skip empty lines + const cols = line.split('\t'); + if (cols.length == 1) return // assign the columns to an object: const data = {}; diff --git a/config/known-ipranges.json b/config/known-ipranges.json index ff4b503..11449a3 100644 --- a/config/known-ipranges.json +++ b/config/known-ipranges.json @@ -9,6 +9,7 @@ {"id": "cloudflare", "name": "Cloudflare Network"}, {"id": "cnisp", "name": "China ISP Range"}, {"id": "cnmob", "name": "China Mobile"}, + {"id": "domtehniki", "name": "Dom Tehniki / WS Telecom"}, {"id": "google", "name": "Google LLC Network"}, {"id": "hetzner", "name": "Hetzner US"}, {"id": "huawei", "name": "Huawei Network"}, @@ -72,6 +73,7 @@ {"from": "138.121.0.0", "to": "138.121.225.254", "m": 16, "g": "misc_sa"}, {"from": "142.147.128.0", "to": "1142.147.255.254", "m": 17, "g": "w2obj"}, {"from": "146.174.128.0", "to": "146.174.191.254", "m": 18, "g": "huawei"}, + {"from": "149.126.192.0", "to": "149.126.223.255", "m": 19, "g": "domtehniki"}, {"from": "149.232.128.0", "to": "149.232.159.254", "m": 19, "g": "huawei"}, {"from": "150.40.128.0", "to": "150.40.255.254", "m": 17, "g": "huawei"}, {"from": "159.138.0.0", "to": "159.138.225.254", "m": 16, "g": "huawei"}, diff --git a/img/captcha.png b/img/captcha.png index c6a8dadb7f980d91a023fe80137c2d1a8fd22053..4ba51c6ac8fd70bff903c5b108a51d01288ae240 100644 GIT binary patch delta 3840 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 nBefI{Qxjn$ZOtorfJ)`R2G8@S-v3 Date: Fri, 31 Oct 2025 10:18:41 +0100 Subject: [PATCH 21/42] Statistics updates --- admin.css | 5 +- admin.js | 250 +++++++++++++++++++++++++++++-------- admin.php | 17 ++- config/default-config.json | 4 +- 4 files changed, 217 insertions(+), 59 deletions(-) diff --git a/admin.css b/admin.css index c50d2ee..37e1952 100644 --- a/admin.css +++ b/admin.css @@ -426,7 +426,7 @@ } /* grid layout for the overview: */ - .botmon_bots_grid, .botmon_webmetrics_grid, .botmon_traffic_grid { + .botmon_bots_grid, .botmon_webmetrics_grid, .botmon_traffic_grid, .botmon_captcha_grid { & { display: grid; grid-gap: 0 .33em; @@ -468,6 +468,9 @@ .botmon_traffic_grid { grid-template-columns: 2fr 1fr; } + .botmon_captcha_grid { + grid-template-columns: 1fr 1fr 1fr; + } /* The tabs bar */ #botmon__tabs ul.tabs li { diff --git a/admin.js b/admin.js index 608225c..05a87a1 100644 --- a/admin.js +++ b/admin.js @@ -518,7 +518,7 @@ BotMon.live = { let visitor = model.findVisitor(dat, type); if (!visitor) { console.info(`No visitor with ID “${dat.id}” found, registering as a new one.`); - visitor = model.registerVisit(dat, type); + visitor = model.registerVisit(dat, type, true); } if (visitor) { // update visitor: @@ -623,6 +623,7 @@ BotMon.live = { bots_whitelisted: 0, humans_blocked: 0, humans_passed: 0, + humans_whitelisted: 0, sus_blocked: 0, sus_passed: 0, sus_whitelisted: 0 @@ -651,7 +652,7 @@ BotMon.live = { // loop over all visitors: model._visitors.forEach( (v) => { - const captchaStr = v._captcha._str(); + const captchaStr = v._captcha._str().replaceAll(/[^YNW]/g, ''); // count total visits and page views: data.visits.total += 1; @@ -663,17 +664,21 @@ BotMon.live = { if (v._type == BM_USERTYPE.KNOWN_BOT) { // known bots - data.visits.bots += 1; - data.views.bots += v._viewCount; this.groups.knownBots.push(v); - // captcha counter - if (captchaStr == 'Y') { - data.captcha.bots_blocked += 1; - } else if (captchaStr == 'YN') { - data.captcha.bots_passed += 1; - } else if (captchaStr == 'W') { - data.captcha.bots_whitelisted += 1; + if (v._seenBy.indexOf(BM_LOGTYPE.SERVER) > -1) { // not for ghost items! + data.visits.bots += 1; + data.views.bots += v._viewCount; + + // captcha counter + if (captchaStr.indexOf('YN') > -1) { + data.captcha.bots_passed += 1; + } else if (captchaStr.indexOf('Y') > -1) { + data.captcha.bots_blocked += 1; + } + if (captchaStr.indexOf('W') > -1) { + data.captcha.bots_whitelisted += 1; + } } } else if (v._type == BM_USERTYPE.KNOWN_USER) { // known users */ @@ -692,32 +697,42 @@ BotMon.live = { if (e.isBot) { // likely bots v._type = BM_USERTYPE.LIKELY_BOT; - data.visits.suspected += 1; - data.views.suspected += v._viewCount; this.groups.suspectedBots.push(v); - // captcha counter - if (captchaStr == 'Y') { - data.captcha.sus_blocked += 1; - } else if (captchaStr == 'YN') { - data.captcha.sus_passed += 1; - } else if (captchaStr == 'W') { - data.captcha.sus_whitelisted += 1; + if (v._seenBy.indexOf(BM_LOGTYPE.SERVER) > -1) { // not for ghost items! + + data.visits.suspected += 1; + data.views.suspected += v._viewCount; + + // captcha counter + if (captchaStr.indexOf('YN') > -1) { + data.captcha.sus_passed += 1; + } else if (captchaStr.indexOf('Y') > -1) { + data.captcha.sus_blocked += 1; + } + if (captchaStr.indexOf('W') > -1) { + data.captcha.sus_whitelisted += 1; + } } } else { // probably humans v._type = BM_USERTYPE.PROBABLY_HUMAN; - data.visits.humans += 1; - data.views.humans += v._viewCount; - this.groups.humans.push(v); - // captcha counter - if (captchaStr == 'Y') { - data.captcha.humans_blocked += 1; - } else if (captchaStr == 'YN') { - data.captcha.humans_passed += 1; + if (v._seenBy.indexOf(BM_LOGTYPE.SERVER) > -1) { // not for ghost items! + data.visits.humans += 1; + data.views.humans += v._viewCount; + + // captcha counter + if (captchaStr.indexOf('YN') > -1) { + data.captcha.humans_passed += 1; + } else if (captchaStr.indexOf('Y') > -1) { + data.captcha.humans_blocked += 1; + } + if (captchaStr.indexOf('W') > -1) { + data.captcha.humans_whitelisted += 1; + } } } } @@ -2011,34 +2026,34 @@ BotMon.live = { if (botsVsHumans) { botsVsHumans.appendChild(makeElement('dt', {}, "Bot statistics")); - for (let i = 0; i <= ( useCaptcha ? 5 : 3 ); i++) { + for (let i = 0; i <= 6; i++) { const dd = makeElement('dd'); let title = ''; let value = ''; switch(i) { case 0: - title = "Total (loads / views / visits):"; - value = (data.loads.total || kNoData) + kSeparator + (data.views.total || kNoData) + kSeparator + (data.visits.total || kNoData); + title = "Known bots visits:"; + value = data.visits.bots || kNoData; break; case 1: - title = "Known bots (views / visits):"; - value = (data.views.bots || kNoData) + kSeparator + (data.visits.bots || kNoData); + title = "Suspected bots visits:"; + value = data.visits.suspected || kNoData; break; case 2: - title = "Suspected bots (views / visits):"; - value = (data.visits.suspected || kNoData) + kSeparator + (data.views.suspected || kNoData) - break; - case 3: - title = "Bots-humans ratio (views / visits):"; - value = BotMon.t._getRatio(data.views.suspected + data.views.bots, data.views.users + data.views.humans, 100) + kSeparator + BotMon.t._getRatio(data.visits.suspected + data.visits.bots, data.visits.users + data.visits.humans, 100); + title = "Bots-humans ratio visits:"; + value = BotMon.t._getRatio(data.visits.suspected + data.visits.bots, data.visits.users + data.visits.humans, 100); break; case 4: - title = "Known bots blocked / passed / whitelisted:"; - value = data.captcha.bots_blocked + kSeparator + data.captcha.bots_passed + kSeparator + data.captcha.bots_whitelisted; + title = "Known bots views:"; + value = data.views.bots || kNoData; break; case 5: - title = "Suspected bots blocked / passed / whitelisted:"; - value = data.captcha.sus_blocked + kSeparator + data.captcha.sus_passed + kSeparator + data.captcha.sus_whitelisted; + title = "Suspected bots views:"; + value = data.views.suspected || kNoData; + break; + case 6: + title = "Bots-humans ratio views:"; + value = BotMon.t._getRatio(data.views.suspected + data.views.bots, data.views.users + data.views.humans, 100); break; default: console.warn(`Unknown list type ${i}.`); @@ -2095,32 +2110,36 @@ BotMon.live = { const wmoverview = document.getElementById('botmon__today__wm_overview'); if (wmoverview) { - const humanVisits = data.views.total; + const humanVisits = data.visits.users + data.visits.humans; const bounceRate = Math.round(100 * (BotMon.live.data.analytics.getBounceCount('users') + BotMon.live.data.analytics.getBounceCount('humans')) / humanVisits); wmoverview.appendChild(makeElement('dt', {}, "Humans’ metrics")); - for (let i = 0; i <= 4; i++) { + for (let i = 0; i <= 5; i++) { const dd = makeElement('dd'); let title = ''; let value = ''; switch(i) { case 0: - title = "Registered users (views / visits):"; - value = (data.views.users || kNoData) + kSeparator + (data.visits.users || kNoData); + title = "Registered users visits:"; + value = data.visits.users || kNoData; break; case 1: - title = "Probably humans (views / visits):"; - value = (data.views.humans || kNoData) + kSeparator + (data.visits.humans || kNoData); + title = "Registered users views:"; + value = data.views.users || kNoData; break; case 2: - title = "Total human page views:"; - value = (data.views.users + data.views.humans) || kNoData; + title = "Probably humans visits:"; + value = data.visits.humans || kNoData; break; case 3: - title = "Total human visits:"; - value = data.views.total || kNoData; + title = "Probably humans views:"; + value = data.views.humans || kNoData; break; case 4: + title = "Total human visits / views"; + value = (data.visits.users + data.visits.humans || kNoData) + kSeparator + ((data.views.users + data.views.humans) || kNoData); + break; + case 5: title = "Humans’ bounce rate:"; value = bounceRate + '%'; break; @@ -2230,6 +2249,131 @@ BotMon.live = { }); } } + + // Update Captcha statistics: + const captchaStatsBlock = document.getElementById('botmon__today__captcha'); + if (captchaStatsBlock) { + + // first column: + const captchaHumans = document.getElementById('botmon__today__cp_humans'); + if (captchaHumans) { + captchaHumans.appendChild(makeElement('dt', {}, "Probably humans:")); + + for (let i = 0; i <= 4; i++) { + const dd = makeElement('dd'); + let title = ''; + let value = ''; + switch(i) { + case 0: + title = "Solved:"; + value = data.captcha.humans_passed; + break; + case 1: + title = "Blocked:"; + value = data.captcha.humans_blocked; + break; + case 2: + title = "Whitelisted:"; + value = data.captcha.humans_whitelisted; + break; + case 3: + title = "Total visits:"; + value = data.visits.humans; + break; + case 4: + title = "Pct. blocked:"; + value = (data.captcha.humans_blocked / data.visits.humans * 100).toFixed(0) + '%'; + break; + default: + console.warn(`Unknown list type ${i}.`); + } + dd.appendChild(makeElement('span', {}, title)); + dd.appendChild(makeElement('strong', {}, value || kNoData)); + captchaHumans.appendChild(dd); + } + + } + + // second column: + const captchaSus = document.getElementById('botmon__today__cp_sus'); + if (captchaSus) { + captchaSus.appendChild(makeElement('dt', {}, "Suspected bots:")); + + for (let i = 0; i <= 4; i++) { + const dd = makeElement('dd'); + let title = ''; + let value = ''; + switch(i) { + case 0: + title = "Solved:"; + value = data.captcha.sus_passed; + break; + case 1: + title = "Blocked:"; + value = data.captcha.sus_blocked; + break; + case 2: + title = "Whitelisted:"; + value = data.captcha.sus_whitelisted; + break; + case 3: + title = "Total visits:"; + value = data.visits.suspected; + break; + case 4: + title = "Pct. blocked:"; + value = (data.captcha.sus_blocked / data.visits.suspected * 100).toFixed(0) + '%'; + break; + default: + console.warn(`Unknown list type ${i}.`); + } + dd.appendChild(makeElement('span', {}, title)); + dd.appendChild(makeElement('strong', {}, value || kNoData)); + captchaSus.appendChild(dd); + } + + } + + // Third column: + const captchaBots = document.getElementById('botmon__today__cp_bots'); + if (captchaBots) { + captchaBots.appendChild(makeElement('dt', {}, "Known bots:")); + + for (let i = 0; i <= 4; i++) { + const dd = makeElement('dd'); + let title = ''; + let value = ''; + switch(i) { + case 0: + title = "Solved:"; + value = data.captcha.bots_passed; + break; + case 1: + title = "Blocked:"; + value = data.captcha.bots_blocked; + break; + case 2: + title = "Whitelisted:"; + value = data.captcha.bots_whitelisted; + break; + case 3: + title = "Total visits:"; + value = data.visits.bots; + break; + case 4: + title = "Pct. blocked:"; + value = (data.captcha.bots_blocked / data.visits.bots * 100).toFixed(0) + '%'; + break; + default: + console.warn(`Unknown list type ${i}.`); + } + dd.appendChild(makeElement('span', {}, title)); + dd.appendChild(makeElement('strong', {}, value || kNoData)); + captchaBots.appendChild(dd); + } + + } + } } }, diff --git a/admin.php b/admin.php index e9eda49..0821f49 100644 --- a/admin.php +++ b/admin.php @@ -34,6 +34,7 @@ class admin_plugin_botmon extends AdminPlugin { // display GeoIP data? $geoIPconf = $this->getConf('geoiplib'); + $useCaptchaConf = ($this->getConf('useCaptcha') !== 'disabled'); $hasOldLogFiles = $this->hasOldLogFiles(); @@ -56,7 +57,7 @@ class admin_plugin_botmon extends AdminPlugin {
    Loading …
    - Overview + Bots overview
    @@ -79,8 +80,18 @@ class admin_plugin_botmon extends AdminPlugin {
    -
    -
    +
    ' . NL; + if ($useCaptchaConf) { + echo '
    + Captcha statistics +
    +
    +
    +
    +
    +
    ' . NL; + } + echo '
    Visitor logs
    diff --git a/config/default-config.json b/config/default-config.json index e7aab9e..6088153 100644 --- a/config/default-config.json +++ b/config/default-config.json @@ -3,7 +3,7 @@ "rules": [ {"func": "fromKnownBotIP", "id": "botIpRange", "desc": "Common Bot IP range", - "bot": 50 + "bot": 40 }, {"func": "matchesClient", "params": ["aol","msie","ffold","chromeold","oldedge","operaold"], "id": "oldClient", "desc": "Obsolete browser version", @@ -75,7 +75,7 @@ }, {"func": "whitelistedByCaptcha", "params": [], "id": "whitelistedByCaptcha", "desc": "Visitor uses a whitelisted IP address", - "bot": -30 + "bot": -20 } ] } \ No newline at end of file From d59c2b76300045c9812c9379e554b616f5abc65b Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Fri, 31 Oct 2025 10:19:05 +0100 Subject: [PATCH 22/42] Date update --- plugin.info.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.info.txt b/plugin.info.txt index cfb04e4..9a3a2b1 100644 --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base botmon author Sascha Leib email ad@hominem.com -date 2025-10-29 +date 2025-10-31 name Bot Monitoring desc A tool for monitoring and analysing bot traffic to your wiki (under development) url https://www.dokuwiki.org/plugin:botmon From 780ef5db6854f28654647cdab69f07e87f30c183 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Fri, 31 Oct 2025 13:12:43 +0100 Subject: [PATCH 23/42] Responsive Design updates --- admin.css | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/admin.css b/admin.css index 37e1952..68a1cd1 100644 --- a/admin.css +++ b/admin.css @@ -419,7 +419,8 @@ .page_icon::before { content: ''; display: inline-block; - width: 20px; height: 20px; + width: 20px; min-width: 20px; max-width: 20px; + height: 20px; background: transparent url('img/page.svg') center no-repeat; background-position: 0 0; background-size: 20px; @@ -435,7 +436,8 @@ dd { display: flex; justify-content: space-between; - align-items: baseline; + align-items: center; + column-gap: .5em; } } } @@ -466,7 +468,22 @@ } } .botmon_traffic_grid { - grid-template-columns: 2fr 1fr; + & { + grid-template-columns: 2fr 1fr; + } + #botmon__today__wm_pages, #botmon__today__wm_referers { + & { + max-width: 100%; + overflow: hidden; + } + dd { + } + dd a { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } } .botmon_captcha_grid { grid-template-columns: 1fr 1fr 1fr; @@ -966,6 +983,23 @@ } } /* layout overrides for narrow screens: */ +@media (max-width: 1024px) { + #botmon__admin #botmon__latest { + .botmon_traffic_grid { + & { + grid-template-columns: 1fr !important; + } + dt { + margin: .5em 0; + } + dl { + border-left: transparent none 0; + padding-left: 0; + } + } + } +} + @media (max-width: 800px) { #botmon__admin #botmon__latest #botmon__today__visitorlists { dl.visitor_details { @@ -989,7 +1023,7 @@ @media (max-width: 670px) { #botmon__admin #botmon__latest { - .botmon_bots_grid, .botmon_webmetrics_grid, .botmon_traffic_grid { + .botmon_bots_grid, .botmon_webmetrics_grid, .botmon_captcha_grid { & { grid-template-columns: 1fr !important; } From ad279a215c0c174eeb976c8d0e216df09c40c48a Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sun, 2 Nov 2025 11:19:58 +0100 Subject: [PATCH 24/42] Captcha improvements Auto-check if languages match; Collect METHOD data in Server-log --- action.php | 5 +++-- admin.css | 2 -- admin.js | 2 +- captcha.js | 23 ++++++++++++++++++++--- 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/action.php b/action.php index c4cd017..62a3c3d 100644 --- a/action.php +++ b/action.php @@ -16,7 +16,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { public function __construct() { // determine if a captcha should be loaded: - $this->showCaptcha = 'Z'; + $this->showCaptcha = 'Z'; // Captcha unknown $useCaptcha = $this->getConf('useCaptcha'); // should we show a captcha? @@ -153,7 +153,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { substr($conf['lang'],0,2), /* page language */ implode(',', array_unique(array_map( function($it) { return substr(trim($it),0,2); }, explode(',',trim($_SERVER['HTTP_ACCEPT_LANGUAGE'], " \t;,*"))))), /* accepted client languages */ $this->getCountryCode(), /* GeoIP country code */ - $this->showCaptcha /* show captcha? */ + $this->showCaptcha, /* show captcha? */ + $_SERVER['REQUEST_METHOD'] ?? '' /* request method */ ); //* create the log line */ diff --git a/admin.css b/admin.css index 68a1cd1..a0c9806 100644 --- a/admin.css +++ b/admin.css @@ -476,8 +476,6 @@ max-width: 100%; overflow: hidden; } - dd { - } dd a { white-space: nowrap; overflow: hidden; diff --git a/admin.js b/admin.js index 05a87a1..a2a4d9a 100644 --- a/admin.js +++ b/admin.js @@ -1845,7 +1845,7 @@ BotMon.live = { switch (type) { case "srv": typeName = "Server"; - columns = ['ts','ip','pg','id','typ','usr','agent','ref','lang','accept','geo','captcha']; + columns = ['ts','ip','pg','id','typ','usr','agent','ref','lang','accept','geo','captcha','method']; break; case "log": typeName = "Page load"; diff --git a/captcha.js b/captcha.js index 9f32475..61958d7 100644 --- a/captcha.js +++ b/captcha.js @@ -6,9 +6,8 @@ const $BMCaptcha = { init: function() { - /* mark the page to contain the captcha styles */ document.getElementsByTagName('body')[0].classList.add('botmon_captcha'); - + $BMCaptcha._cbDly = 1.5; $BMCaptcha.install() }, @@ -51,7 +50,8 @@ const $BMCaptcha = { bm_parent.appendChild(dlg); // call the delayed callback in a couple of seconds: - setTimeout($BMCaptcha._delayedCallback, 1500); + $BMCaptcha._st = performance.now(); + setTimeout($BMCaptcha._delayedCallback, $BMCaptcha._cbDly * 1000); }, /* creates a digest hash for the cookie function */ @@ -175,6 +175,7 @@ const $BMCaptcha = { document._botmon.ip || '0.0.0.0', (new Date()).toISOString().substring(0, 10) ]; + if ($BMCaptcha._st - performance.now() >= 0) dat.push($BMCaptcha._st - performance.now()); const hash = $BMCaptcha.digest.hash(dat.join('|')); // set the cookie: @@ -208,10 +209,26 @@ const $BMCaptcha = { if (input) { input.removeAttribute('disabled'); input.focus(); + setTimeout($BMCaptcha._autoCheck, 200, input); } } }, + _cbDly: null, + _st: null, + _autoCheck: function(e) { + + let bPass = 0; + const threshold = 1; + + const pLang = document.documentElement.lang || 'en'; + if (pLang !== 'en') { + const cntLangs = navigator.languages.map(lang => lang.split('-')[0]); + if (cntLangs.indexOf(pLang) >= 0) bPass += 1; + } + + if (bPass >= threshold) e.click(); + } } // initialise the captcha module: $BMCaptcha.init(); \ No newline at end of file From fb281ca1a13fb0419ac09220cd216d20c52045d5 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sun, 2 Nov 2025 21:22:38 +0100 Subject: [PATCH 25/42] Captcha updates --- action.php | 7 ++++++- admin.js | 20 +++++++++++++++----- admin.php | 14 +++++++------- captcha.js | 11 +++++------ conf/default.php | 1 + conf/metadata.php | 7 +++++++ lang/en/settings.php | 3 +++ plugin.info.txt | 2 +- 8 files changed, 45 insertions(+), 20 deletions(-) diff --git a/action.php b/action.php index 62a3c3d..0d9bfdb 100644 --- a/action.php +++ b/action.php @@ -263,7 +263,12 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { echo DOKU_TAB . DOKU_TAB . '"dlgLoading": ' . json_encode($this->getLang('bm_dlgLoading')) . ',' . NL; echo DOKU_TAB . DOKU_TAB . '"dlgError": ' . json_encode($this->getLang('bm_dlgError')) . ',' . NL; echo DOKU_TAB . '};' . NL; - + + // captcha configuration options + echo DOKU_TAB . '$BMConfig = {' . NL; + echo DOKU_TAB . DOKU_TAB . '"captchaBypass": ' . json_encode($this->getConf('captchaBypass')) . NL; + echo DOKU_TAB . '};' . NL; + echo '' . NL; } } diff --git a/admin.js b/admin.js index a2a4d9a..0a7234f 100644 --- a/admin.js +++ b/admin.js @@ -1712,6 +1712,16 @@ BotMon.live = { return visitor.hasOwnProperty('_ipRange'); }, + // is the IP address from a specifin known ISP network + fromISPRange: function(visitor, ...isps) { + if (visitor.hasOwnProperty('_ipRange')) { + if (isps.indexOf(visitor._ipRange.g) > -1) { + return true; + } + } + return false; + }, + // is the page language mentioned in the client's accepted languages? // the parameter holds an array of exceptions, i.e. page languages that should be ignored. matchLang: function(visitor, ...exceptions) { @@ -2026,7 +2036,7 @@ BotMon.live = { if (botsVsHumans) { botsVsHumans.appendChild(makeElement('dt', {}, "Bot statistics")); - for (let i = 0; i <= 6; i++) { + for (let i = 0; i <= 5; i++) { const dd = makeElement('dd'); let title = ''; let value = ''; @@ -2043,15 +2053,15 @@ BotMon.live = { title = "Bots-humans ratio visits:"; value = BotMon.t._getRatio(data.visits.suspected + data.visits.bots, data.visits.users + data.visits.humans, 100); break; - case 4: + case 3: title = "Known bots views:"; value = data.views.bots || kNoData; break; - case 5: + case 4: title = "Suspected bots views:"; value = data.views.suspected || kNoData; break; - case 6: + case 5: title = "Bots-humans ratio views:"; value = BotMon.t._getRatio(data.views.suspected + data.views.bots, data.views.users + data.views.humans, 100); break; @@ -2771,7 +2781,7 @@ BotMon.live = { /* bot evaluation rating */ if (data._type !== BM_USERTYPE.KNOWN_BOT && data._type !== BM_USERTYPE.KNOWN_USER) { dl.appendChild(make('dt', undefined, "Bot rating:")); - dl.appendChild(make('dd', {'class': 'bot-rating'}, ( data._botVal ? data._botVal : '–' ) + ' (of ' + BotMon.live.data.rules._threshold + ')')); + dl.appendChild(make('dd', {'class': 'bot-rating'}, ( data._botVal ? data._botVal : '0' ) + ' (of ' + BotMon.live.data.rules._threshold + ')')); /* add bot evaluation details: */ if (data._eval) { diff --git a/admin.php b/admin.php index 0821f49..8fa5893 100644 --- a/admin.php +++ b/admin.php @@ -53,6 +53,13 @@ class admin_plugin_botmon extends AdminPlugin {
    +

    Latest data

    Loading …
    @@ -116,13 +123,6 @@ class admin_plugin_botmon extends AdminPlugin { } echo DOKU_TAB . DOKU_TAB . '' . NL . DOKU_TAB . '
    ' . NL; - echo DOKU_TAB . '' . NL; echo '
    '; } diff --git a/captcha.js b/captcha.js index 61958d7..778cef6 100644 --- a/captcha.js +++ b/captcha.js @@ -218,16 +218,15 @@ const $BMCaptcha = { _autoCheck: function(e) { - let bPass = 0; - const threshold = 1; + const bypass = ($BMConfig['captchaBypass'] || '').split(','); + var action = false; - const pLang = document.documentElement.lang || 'en'; - if (pLang !== 'en') { + if (bypass.indexOf('langmatch') >= 0) { // Languages matching const cntLangs = navigator.languages.map(lang => lang.split('-')[0]); - if (cntLangs.indexOf(pLang) >= 0) bPass += 1; + if (cntLangs.indexOf(document.documentElement.lang || 'en') >= 0) action = true; } - if (bPass >= threshold) e.click(); + if (action) e.click(); // action! } } // initialise the captcha module: diff --git a/conf/default.php b/conf/default.php index 4ae29b2..9c196dd 100644 --- a/conf/default.php +++ b/conf/default.php @@ -10,3 +10,4 @@ $conf['combineNets'] = true; $conf['geoiplib'] = 'disabled'; $conf['useCaptcha'] = 'disabled'; $conf['captchaSeed'] = 'c53bc5f94929451987efa6c768d8856b'; +$conf['captchaBypass'] = ''; diff --git a/conf/metadata.php b/conf/metadata.php index de95acc..8ae3c4c 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -5,14 +5,21 @@ * @author Sascha Leib */ +// How to show data in the admin interface: $meta['showday'] = array('multichoice', '_choices' => array ('yesterday', 'today')); $meta['combineNets'] = array('onoff'); +// Geolocation settings: $meta['geoiplib'] = array('multichoice', '_choices' => array ('disabled', 'phpgeoip')); +// Captcha settings: $meta['useCaptcha'] = array('multichoice', '_choices' => array ('disabled', 'loremipsum', 'dada')); + $meta['captchaSeed'] = array('string'); + +$meta['captchaBypass'] = array('multicheckbox', + '_choices' => array ('langmatch'), '_other' => 'exists'); diff --git a/lang/en/settings.php b/lang/en/settings.php index 42a2d20..67a8835 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -21,3 +21,6 @@ $lang['useCaptcha'] = 'Enable Captcha
    (Experimental, read the manual $lang['useCaptcha_o_dada'] = 'Captcha with Dada placeholder text'; $lang['captchaSeed'] = 'Captcha Seed
    (Enter a random string, e.g. from here)'; + +$lang['captchaBypass'] = 'Automatically solve the Captcha, if …
    (Note: this probably does not make much sense for English wikis!)'; + $lang['captchaBypass_langmatch'] = 'Client and page languages match '; diff --git a/plugin.info.txt b/plugin.info.txt index 9a3a2b1..c960c44 100644 --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base botmon author Sascha Leib email ad@hominem.com -date 2025-10-31 +date 2025-11-02 name Bot Monitoring desc A tool for monitoring and analysing bot traffic to your wiki (under development) url https://www.dokuwiki.org/plugin:botmon From 0b2c568aafd9da5d4ca17412f6ac2dac1907994c Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Mon, 3 Nov 2025 21:34:13 +0100 Subject: [PATCH 26/42] Temp test --- action.php | 2 +- config/known-bots.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/action.php b/action.php index 0d9bfdb..1c7a06f 100644 --- a/action.php +++ b/action.php @@ -310,7 +310,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today; $expected = hash('sha256', $raw); - //echo '
    • cookie: ' . $cookieVal . '
    • expected: ' . $expected . '
    • matches: ' .($cookieVal == $expected ? 'true' : 'false') . '
    '; + echo '' . NL; return $cookieVal == $expected; } diff --git a/config/known-bots.json b/config/known-bots.json index 0688a75..a5964c5 100644 --- a/config/known-bots.json +++ b/config/known-bots.json @@ -127,7 +127,7 @@ {"id": "metauser", "n": "Meta/Facebook User", "r": ["facebookexternalhit","facebookcatalog"], - "rx": ["facebookexternalhit\\/(\\d+\\.?\\d*)", "meta-externalfetcher\\/(\\d\\.\\d)", "facebookcatalog\\/(\\d+\\.?\\d*)"], + "rx": ["facebook\\w*\\/(\\d+\\.?\\d*)", "meta\\-?\\w*\\/(\\d\\.\\d)"], "url": "https://developers.facebook.com/docs/sharing/webmasters/crawler" }, {"id": "qwant", From a9120ea20fb3b0e74764a82131579a9f0223eaf2 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Mon, 3 Nov 2025 21:40:39 +0100 Subject: [PATCH 27/42] Minor fix --- action.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/action.php b/action.php index 1c7a06f..c33a63e 100644 --- a/action.php +++ b/action.php @@ -310,7 +310,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today; $expected = hash('sha256', $raw); - echo '' . NL; + //echo '' . NL; return $cookieVal == $expected; } From 6728cfa61cd62383ade8f0066241c9b61cdbe241 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Mon, 3 Nov 2025 22:21:41 +0100 Subject: [PATCH 28/42] Debugging Help Log captcha data --- action.php | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/action.php b/action.php index c33a63e..cb3830d 100644 --- a/action.php +++ b/action.php @@ -301,6 +301,13 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $this->showCaptcha = $cCode; // store the captcha code for the logfile } + /** + * Checks if the user has a valid captcha cookie. + * + * @return boolean + * @access private + * + **/ private function hasCaptchaCookie() { $cookieVal = isset($_COOKIE['DWConfirm']) ? $_COOKIE['DWConfirm'] : null; @@ -310,11 +317,48 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today; $expected = hash('sha256', $raw); - //echo '' . NL; + // for debugging: write captcha data to the log: + $this->writeCaptchaLog($_SERVER['REMOTE_ADDR'], $cookieVal, $_SERVER['SERVER_NAME'], $expected); return $cookieVal == $expected; } + /** + * Writes data to the captcha log. + * + * @return void + */ + private function writeCaptchaLog($remote_addr, $cookieVal, $serverName, $expected) { + + $logArr = Array( + $remote_addr, /* remote IP */ + $cookieVal, /* cookie value */ + $this->getConf('captchaSeed'), /* seed */ + $serverName, /* server name */ + $expected, /* expected cookie value */ + $cookieVal == $expected /* cookie matches expected value? */ + ); + + //* create the log line */ + $filename = __DIR__ .'/logs/' . gmdate('Y-m-d') . '.captcha.txt'; /* use GMT date for filename */ + $logline = gmdate('Y-m-d H:i:s'); /* use GMT time for log entries */ + foreach ($logArr as $tab) { + $logline .= "\t" . $tab; + }; + + /* write the log line to the file */ + $logfile = fopen($filename, 'a'); + if (!$logfile) die(); + if (fwrite($logfile, $logline . "\n") === false) { + fclose($logfile); + die(); + } + + /* Done */ + fclose($logfile); + } + + // check if the visitor's IP is on a whitelist: private function captchaWhitelisted() { From 83d954bd67158f2c9e0bab01d9f5d997e17ba26d Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Tue, 4 Nov 2025 09:25:12 +0100 Subject: [PATCH 29/42] Debug version Temporarily disable hashing for bug search. --- .gitignore | 1 + action.php | 2 +- captcha.js | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index a377e1b..61335d4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ logs/*.log.txt logs/*.srv.txt logs/*.tck.txt +logs/*.captcha.txt config/user-*.json php_errors.log diff --git a/action.php b/action.php index cb3830d..e9f7652 100644 --- a/action.php +++ b/action.php @@ -315,7 +315,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $today = substr((new DateTime())->format('c'), 0, 10); $raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today; - $expected = hash('sha256', $raw); + $expected = $raw; //hash('sha256', $raw); // for debugging: write captcha data to the log: $this->writeCaptchaLog($_SERVER['REMOTE_ADDR'], $cookieVal, $_SERVER['SERVER_NAME'], $expected); diff --git a/captcha.js b/captcha.js index 778cef6..4ff05c0 100644 --- a/captcha.js +++ b/captcha.js @@ -175,8 +175,8 @@ const $BMCaptcha = { document._botmon.ip || '0.0.0.0', (new Date()).toISOString().substring(0, 10) ]; - if ($BMCaptcha._st - performance.now() >= 0) dat.push($BMCaptcha._st - performance.now()); - const hash = $BMCaptcha.digest.hash(dat.join('|')); + //if ($BMCaptcha._st - performance.now() >= 0) dat.push($BMCaptcha._st - performance.now()); + const hash = /*$BMCaptcha.digest.hash(*/dat.join('|')/*)*/; // set the cookie: document.cookie = "DWConfirm=" + encodeURIComponent(hash) + ';path=/;hostOnly;session;sameSite=strict;' From 4ab56ef99849fca2b266a78bb155fca2206bd97b Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Tue, 4 Nov 2025 20:23:48 +0100 Subject: [PATCH 30/42] Simplified captcha cookie Until I found that pesky bug! --- action.php | 7 +++++-- captcha.js | 9 +++++---- config/default-config.json | 8 ++++++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/action.php b/action.php index e9f7652..2419061 100644 --- a/action.php +++ b/action.php @@ -314,7 +314,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $today = substr((new DateTime())->format('c'), 0, 10); - $raw = $this->getConf('captchaSeed') . '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today; + $raw = $this->getConf('captchaSeed') /*. '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today */; $expected = $raw; //hash('sha256', $raw); // for debugging: write captcha data to the log: @@ -330,13 +330,16 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { */ private function writeCaptchaLog($remote_addr, $cookieVal, $serverName, $expected) { + global $INFO; + $logArr = Array( $remote_addr, /* remote IP */ $cookieVal, /* cookie value */ $this->getConf('captchaSeed'), /* seed */ $serverName, /* server name */ $expected, /* expected cookie value */ - $cookieVal == $expected /* cookie matches expected value? */ + ($cookieVal == $expected ? 'MATCH' : 'WRONG'), /* cookie matches expected value? */ + $_SERVER['REQUEST_URI'] /* request URI */ ); //* create the log line */ diff --git a/captcha.js b/captcha.js index 4ff05c0..89066b6 100644 --- a/captcha.js +++ b/captcha.js @@ -168,15 +168,16 @@ const $BMCaptcha = { try { var $status = 'loading'; - // generate the hash: - const dat = [ // the data to encode + // generate the hash: -- disabled until I found the pesky bug in the digest + /*const dat = [ // the data to encode document._botmon.seed || '', location.hostname, document._botmon.ip || '0.0.0.0', (new Date()).toISOString().substring(0, 10) - ]; + ]; */ //if ($BMCaptcha._st - performance.now() >= 0) dat.push($BMCaptcha._st - performance.now()); - const hash = /*$BMCaptcha.digest.hash(*/dat.join('|')/*)*/; + //const hash = $BMCaptcha.digest.hash(dat.join('|')); + const hash = document._botmon.seed || '' // set the cookie: document.cookie = "DWConfirm=" + encodeURIComponent(hash) + ';path=/;hostOnly;session;sameSite=strict;' diff --git a/config/default-config.json b/config/default-config.json index 6088153..01e3a74 100644 --- a/config/default-config.json +++ b/config/default-config.json @@ -2,7 +2,7 @@ "threshold": 100, "rules": [ {"func": "fromKnownBotIP", - "id": "botIpRange", "desc": "Common Bot IP range", + "id": "knownIpRange", "desc": "From known IP range", "bot": 40 }, {"func": "matchesClient", "params": ["aol","msie","ffold","chromeold","oldedge","operaold"], @@ -57,9 +57,13 @@ "id": "impPC", "desc": "Impossible combination of platform and client", "bot": 70 }, + {"func": "loadSpeed", "params": [3, 10], + "id": "speedRun", "desc": "Average time between page loads is less than 10 seconds", + "bot": 30 + }, {"func": "loadSpeed", "params": [3, 20], "id": "speedRun", "desc": "Average time between page loads is less than 20 seconds", - "bot": 60 + "bot": 30 }, {"func": "noAcceptLang", "id": "noAcc", "desc": "No “Accept-Language” header", From 2289428390b93e247f5b1f76e4de813d84318ced Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Wed, 5 Nov 2025 13:22:59 +0100 Subject: [PATCH 31/42] User data details --- admin.css | 7 +++-- admin.js | 83 ++++++++++++++++++++++++------------------------- captcha.js | 4 +-- plugin.info.txt | 2 +- 4 files changed, 48 insertions(+), 48 deletions(-) diff --git a/admin.css b/admin.css index a0c9806..40175ad 100644 --- a/admin.css +++ b/admin.css @@ -675,7 +675,7 @@ grid-template-columns: min-content auto; gap: .25em .5em; border-left: transparent none 0; - margin: 0 .5rem .25rem 0; + margin: .5rem .5rem .25rem 0; } dt { grid-column: 1; @@ -686,8 +686,6 @@ background-color: transparent; } dd.pages { - & { - } ul { li { & { @@ -700,6 +698,9 @@ &:nth-child(odd) { background-color: #DFDFDF; } + &.detailled { + outline: red dotted 1pt; + } div.row { display: flex; flex-direction: row; diff --git a/admin.js b/admin.js index 0a7234f..204af86 100644 --- a/admin.js +++ b/admin.js @@ -2658,6 +2658,7 @@ BotMon.live = { if (data.ip == '127.0.0.1' || data.ip == '::1' ) ipType = '0'; const platformName = (data._platform ? data._platform.n : 'Unknown'); const clientName = (data._client ? data._client.n: 'Unknown'); + const combinedItem = data.hasOwnProperty('_ipRange'); const dl = make('dl', {'class': 'visitor_details'}); @@ -2677,7 +2678,7 @@ BotMon.live = { } - } else { /* not for bots */ + } else if (!combinedItem) { /* not for bots or combined items */ dl.appendChild(make('dt', {}, "Client:")); /* client */ dl.appendChild(make('dd', {'class': 'has_icon client cl_' + (data._client ? data._client.id : 'unknown')}, @@ -2687,26 +2688,41 @@ BotMon.live = { dl.appendChild(make('dd', {'class': 'has_icon platform pf_' + (data._platform ? data._platform.id : 'unknown')}, platformName + ( data._platform.v > 0 ? ' (' + data._platform.v + ')' : '' ) )); - /*dl.appendChild(make('dt', {}, "ID:")); - dl.appendChild(make('dd', {'class': 'has_icon ip' + data.typ}, data.id));*/ - } + dl.appendChild(make('dt', {}, "IP-Address:")); + const ipItem = make('dd', {'class': 'has_icon ipaddr ip' + ipType}); + ipItem.appendChild(make('span', {'class': 'address'} , data.ip)); + ipItem.appendChild(make('a', { + 'class': 'icon_only extlink ipinfo', + 'href': `https://ipinfo.io/${encodeURIComponent(data.ip)}`, + 'target': 'ipinfo', + 'title': "View this address on IPInfo.io" + } , "DNS Info")); + ipItem.appendChild(make('a', { + 'class': 'icon_only extlink abuseipdb', + 'href': `https://www.abuseipdb.com/check/${encodeURIComponent(data.ip)}`, + 'target': 'abuseipdb', + 'title': "Check this address on AbuseIPDB.com" + } , "Check on AbuseIPDB")); + dl.appendChild(ipItem); - dl.appendChild(make('dt', {}, "IP-Address:")); - const ipItem = make('dd', {'class': 'has_icon ipaddr ip' + ipType}); - ipItem.appendChild(make('span', {'class': 'address'} , data.ip)); - ipItem.appendChild(make('a', { - 'class': 'icon_only extlink ipinfo', - 'href': `https://ipinfo.io/${encodeURIComponent(data.ip)}`, - 'target': 'ipinfo', - 'title': "View this address on IPInfo.io" - } , "DNS Info")); - ipItem.appendChild(make('a', { - 'class': 'icon_only extlink abuseipdb', - 'href': `https://www.abuseipdb.com/check/${encodeURIComponent(data.ip)}`, - 'target': 'abuseipdb', - 'title': "Check this address on AbuseIPDB.com" - } , "Check on AbuseIPDB")); - dl.appendChild(ipItem); + dl.appendChild(make('dt', {}, "User-Agent:")); + dl.appendChild(make('dd', {'class': 'agent'}, data.agent)); + + dl.appendChild(make('dt', {}, "Languages:")); + dl.appendChild(make('dd', {'class': 'langs'}, ` [${data.accept}]`)); + + dl.appendChild(make('dt', {}, "Session ID:")); + dl.appendChild(make('dd', {'class': 'has_icon session typ_' + data.typ}, data.id)); + + if (data.geo && data.geo !=='') { + dl.appendChild(make('dt', {}, "Location:")); + dl.appendChild(make('dd', { + 'class': 'has_icon country ctry_' + data.geo.toLowerCase(), + 'data-ctry': data.geo, + 'title': "Country: " + data._country + }, data._country + ' (' + data.geo + ')')); + } + } if (Math.abs(data._lastSeen - data._firstSeen) < 100) { dl.appendChild(make('dt', {}, "Seen:")); @@ -2721,13 +2737,10 @@ BotMon.live = { dl.appendChild(make('dt', {}, "Actions:")); dl.appendChild(make('dd', {'class': 'views'}, "Page loads: " + data._loadCount.toString() + - ( data._captcha['Y'] > 0 || data._captcha['W'] || data._captcha['-'] > 0 ? ", captchas: " + (data._captcha['Y']+data._captcha['W']+data._captcha['-']).toString() : '') + + ( data._captcha['Y'] > 0 ? ", captchas: " + data._captcha['Y'].toString() : '') + ", views: " + data._viewCount.toString() )); - dl.appendChild(make('dt', {}, "User-Agent:")); - dl.appendChild(make('dd', {'class': 'agent'}, data.agent)); - if (data.ref && data.ref !== '') { dl.appendChild(make('dt', {}, "Referrer:")); @@ -2741,18 +2754,6 @@ BotMon.live = { }, data.ref)); } - dl.appendChild(make('dt', {}, "Languages:")); - dl.appendChild(make('dd', {'class': 'langs'}, ` [${data.accept}]`)); - - if (data.geo && data.geo !=='') { - dl.appendChild(make('dt', {}, "Location:")); - dl.appendChild(make('dd', { - 'class': 'has_icon country ctry_' + data.geo.toLowerCase(), - 'data-ctry': data.geo, - 'title': "Country: " + data._country - }, data._country + ' (' + data.geo + ')')); - } - if (data.captcha && data.captcha !=='') { dl.appendChild(make('dt', {}, "Captcha-status:")); dl.appendChild(make('dd', { @@ -2760,9 +2761,6 @@ BotMon.live = { }, model._makeCaptchaTitle(data._captcha))); } - dl.appendChild(make('dt', {}, "Session ID:")); - dl.appendChild(make('dd', {'class': 'has_icon session typ_' + data.typ}, data.id)); - dl.appendChild(make('dt', {}, "Seen by:")); dl.appendChild(make('dd', {'class': 'has_icon seenby sb_' + data._seenBy.join('')}, data._seenBy.join(', ') )); @@ -2773,7 +2771,7 @@ BotMon.live = { /* list all page views */ data._pageViews.sort( (a, b) => a._firstSeen - b._firstSeen ); data._pageViews.forEach( (page) => { - pageList.appendChild(BotMon.live.gui.lists._makePageViewItem(page)); + pageList.appendChild(BotMon.live.gui.lists._makePageViewItem(page, combinedItem)); }); pagesDd.appendChild(pageList); dl.appendChild(pagesDd); @@ -2835,14 +2833,15 @@ BotMon.live = { }, // make a page view item: - _makePageViewItem: function(page) { - //console.log("makePageViewItem:",page); + _makePageViewItem: function(page, moreInfo) { + console.log("makePageViewItem:",page); // shortcut for neater code: const make = BotMon.t._makeElement; // the actual list item: const pgLi = make('li'); + if (moreInfo) pgLi.classList.add('detailled'); const row1 = make('div', {'class': 'row'}); diff --git a/captcha.js b/captcha.js index 89066b6..bff2b1d 100644 --- a/captcha.js +++ b/captcha.js @@ -180,8 +180,8 @@ const $BMCaptcha = { const hash = document._botmon.seed || '' // set the cookie: - document.cookie = "DWConfirm=" + encodeURIComponent(hash) + ';path=/;hostOnly;session;sameSite=strict;' - + (document.location.protocol === 'https:' ? 'secure;' : ''); + document.cookie = "DWConfirm=" + encodeURIComponent(hash) + '; path=/; hostOnly; session; sameSite=strict;' + + (document.location.protocol === 'https:' ? ' secure;' : ''); } catch (err) { console.error(err); diff --git a/plugin.info.txt b/plugin.info.txt index c960c44..39c5c18 100644 --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base botmon author Sascha Leib email ad@hominem.com -date 2025-11-02 +date 2025-11-05 name Bot Monitoring desc A tool for monitoring and analysing bot traffic to your wiki (under development) url https://www.dokuwiki.org/plugin:botmon From 9b2115f1b770a0edca3bc72f38b7bf38be797609 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Wed, 5 Nov 2025 18:28:08 +0100 Subject: [PATCH 32/42] Debug Cookies --- action.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/action.php b/action.php index 2419061..8975ae2 100644 --- a/action.php +++ b/action.php @@ -357,7 +357,16 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { die(); } - /* Done */ + // in case of errors, write the cookie data to the log: + if (!$cookieVal) { + $logline .= "\t" . json_encode(print_r($_COOKIE, true)); + if (fwrite($logfile, $logline . "\n") === false) { + fclose($logfile); + die(); + } + } + + /* Done. close the file. */ fclose($logfile); } From 815e0e16ba515d9c121796245cf5079a4a2a1eaf Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Wed, 5 Nov 2025 18:31:14 +0100 Subject: [PATCH 33/42] Cookie debug 2 --- captcha.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/captcha.js b/captcha.js index bff2b1d..538587a 100644 --- a/captcha.js +++ b/captcha.js @@ -180,7 +180,7 @@ const $BMCaptcha = { const hash = document._botmon.seed || '' // set the cookie: - document.cookie = "DWConfirm=" + encodeURIComponent(hash) + '; path=/; hostOnly; session; sameSite=strict;' + document.cookie = "DWConfirm=" + encodeURIComponent(hash) + '; path=/; session;' + (document.location.protocol === 'https:' ? ' secure;' : ''); } catch (err) { From 0cfc0c5d33cfc511c3bd8bf17a4147e217b7d21a Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Wed, 5 Nov 2025 18:43:33 +0100 Subject: [PATCH 34/42] Cookie debug 3 --- action.php | 2 +- captcha.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/action.php b/action.php index 8975ae2..a803455 100644 --- a/action.php +++ b/action.php @@ -359,7 +359,7 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { // in case of errors, write the cookie data to the log: if (!$cookieVal) { - $logline .= "\t" . json_encode(print_r($_COOKIE, true)); + $logline = print_r($_COOKIE, true); if (fwrite($logfile, $logline . "\n") === false) { fclose($logfile); die(); diff --git a/captcha.js b/captcha.js index 538587a..3228eb9 100644 --- a/captcha.js +++ b/captcha.js @@ -191,8 +191,8 @@ const $BMCaptcha = { // change the interface: const dlg = document.getElementById('botmon_captcha_box'); if (dlg) { - dlg.classList.remove('ready'); dlg.classList.add( $status ); + dlg.classList.remove('ready'); } // reload the page: @@ -203,8 +203,8 @@ const $BMCaptcha = { _delayedCallback: function() { const dlg = document.getElementById('botmon_captcha_box'); if (dlg) { - dlg.classList.remove('checking'); dlg.classList.add('ready'); + dlg.classList.remove('checking'); const input = dlg.getElementsByTagName('input')[0]; if (input) { From 871c97bf140abd3a91faed9b3d9e8bef378f014e Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Fri, 7 Nov 2025 12:29:08 +0100 Subject: [PATCH 35/42] Multiple smaller fixes Added Support for Privacy Browser (Android), Split Meta bots for better control, Added Internet Archive to default whitelist, Reactivated hashed cookies. --- .gitignore | 2 +- action.php | 4 ++-- admin.css | 1 + captcha.js | 13 ++++++------- config/default-whitelist.txt | 12 ++++++++++-- config/known-bots.json | 14 +++++++------- config/known-clients.json | 4 ++++ img/captcha.png | Bin 4104 -> 4571 bytes img/clients.png | Bin 24898 -> 26161 bytes 9 files changed, 31 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 61335d4..9a5c131 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ logs/*.log.txt logs/*.srv.txt logs/*.tck.txt logs/*.captcha.txt -config/user-*.json +config/user-*.* php_errors.log diff --git a/action.php b/action.php index a803455..2f27af2 100644 --- a/action.php +++ b/action.php @@ -314,8 +314,8 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { $today = substr((new DateTime())->format('c'), 0, 10); - $raw = $this->getConf('captchaSeed') /*. '|' . $_SERVER['SERVER_NAME'] . '|' . $_SERVER['REMOTE_ADDR'] . '|' . $today */; - $expected = $raw; //hash('sha256', $raw); + $raw = $this->getConf('captchaSeed') . ';' . $_SERVER['SERVER_NAME'] . ';' . $_SERVER['REMOTE_ADDR'] . ';' . $today; + $expected = hash('sha256', $raw); // for debugging: write captcha data to the log: $this->writeCaptchaLog($_SERVER['REMOTE_ADDR'], $cookieVal, $_SERVER['SERVER_NAME'], $expected); diff --git a/admin.css b/admin.css index 40175ad..899d43a 100644 --- a/admin.css +++ b/admin.css @@ -113,6 +113,7 @@ &.cl_operaold::before { background-position-y: -380px } &.cl_wget::before { background-position-y: -400px } &.cl_python::before { background-position-y: -420px } + &.cl_privacybrowser::before { background-position-y: -440px } &.cl_other::before { background-image: url('img/more.svg') } /* Captcha statuses */ diff --git a/captcha.js b/captcha.js index 3228eb9..c818c97 100644 --- a/captcha.js +++ b/captcha.js @@ -54,7 +54,7 @@ const $BMCaptcha = { setTimeout($BMCaptcha._delayedCallback, $BMCaptcha._cbDly * 1000); }, - /* creates a digest hash for the cookie function */ + /* creates a digest hash */ digest: { /* simple SHA hash function - adapted from https://geraintluff.github.io/sha256/ */ @@ -168,16 +168,15 @@ const $BMCaptcha = { try { var $status = 'loading'; - // generate the hash: -- disabled until I found the pesky bug in the digest - /*const dat = [ // the data to encode + // generate the hash: + const dat = [ // the data to encode document._botmon.seed || '', location.hostname, document._botmon.ip || '0.0.0.0', (new Date()).toISOString().substring(0, 10) - ]; */ - //if ($BMCaptcha._st - performance.now() >= 0) dat.push($BMCaptcha._st - performance.now()); - //const hash = $BMCaptcha.digest.hash(dat.join('|')); - const hash = document._botmon.seed || '' + ]; + if (performance.now() - $BMCaptcha._st <= 1500) dat.push(performance.now() - $BMCaptcha._st); + const hash = $BMCaptcha.digest.hash(dat.join(';')); // set the cookie: document.cookie = "DWConfirm=" + encodeURIComponent(hash) + '; path=/; session;' diff --git a/config/default-whitelist.txt b/config/default-whitelist.txt index 093087f..8d66153 100644 --- a/config/default-whitelist.txt +++ b/config/default-whitelist.txt @@ -1,3 +1,11 @@ +# Internet Archive Bot Ranges +207.241.224.0 207.241.239.255 20 +207.241.224.0 207.241.224.255 24 +207.241.231.0 207.241.231.255 24 +207.241.234.0 207.241.234.255 24 +207.241.237.0 207.241.237.255 24 +208.70.24.0 208.70.31.255 21 + # Bing Bot IP ranges - taken from https://www.bing.com/toolbox/bingbot.json 157.55.39.0 157.55.39.255 24 207.46.13.0 207.46.13.255 24 @@ -342,5 +350,5 @@ 2a02:0598:0096:8a00:0000:0000:1200:0120 2a02:0598:0096:8a00:0000:0000:1200:013f 123 # localhosts -#127.0.0.1 127.255.255.255 8 -#::1 ::1 128 \ No newline at end of file +127.0.0.1 127.255.255.255 8 +::1 ::1 128 \ No newline at end of file diff --git a/config/known-bots.json b/config/known-bots.json index a5964c5..31ec325 100644 --- a/config/known-bots.json +++ b/config/known-bots.json @@ -118,16 +118,16 @@ "rx": ["Perplexity\\-User\\/(\\d+\\.\\d+);"], "url": "https://perplexity.ai/perplexitybot" }, - {"id": "metabots", - "n": "Meta/Facebook", - "r": ["meta-webindexer","meta-externalads","meta-externalagent"], - "rx": ["facebook[cw]\\w+\\/(\\d+\\.\\d+)", "meta-externala\\w+\\/(\\d+\\.\\d+)"], - "url": "https://developers.facebook.com/docs/sharing/webmasters/crawler" - }, {"id": "metauser", "n": "Meta/Facebook User", "r": ["facebookexternalhit","facebookcatalog"], - "rx": ["facebook\\w*\\/(\\d+\\.?\\d*)", "meta\\-?\\w*\\/(\\d\\.\\d)"], + "rx": ["facebookexternalhit\\/(\\d+\\.?\\d*)", "facebookcatalog\\/(\\d\\.?\\d*)"], + "url": "https://developers.facebook.com/docs/sharing/webmasters/crawler" + }, + {"id": "metabots", + "n": "Meta/Facebook", + "r": ["meta-webindexer","meta-externalads","meta-externalagent", "meta-webindexer"], + "rx": ["facebook[cw]\\w+\\/(\\d+\\.?\\d*)", "meta\\-[cw]\\w+\\/(\\d+\\.?\\d*)", "meta-externalads\\/(\\d+\\.?\\d*)", "meta-externalagent\\/(\\d+\\.?\\d*)"], "url": "https://developers.facebook.com/docs/sharing/webmasters/crawler" }, {"id": "qwant", diff --git a/config/known-clients.json b/config/known-clients.json index 9f35745..a3ac86c 100644 --- a/config/known-clients.json +++ b/config/known-clients.json @@ -102,5 +102,9 @@ {"n": "wget", "id": "wget", "rx": [ "Wget\\/(\\d+\\.?\\d*\\.?\\d*)" ] + }, + {"n": "PrivacyBrowser", + "id": "privacybrowser", + "rx": [ "PrivacyBrowser\\/(\\d+\\.?\\d*)" ] } ] \ No newline at end of file diff --git a/img/captcha.png b/img/captcha.png index 4ba51c6ac8fd70bff903c5b108a51d01288ae240..aec43eb5fb3c02d0ce30c5e420c69f6f7128ad7f 100644 GIT binary patch delta 4311 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 z zf;a*)kQ+Tam-HFup{}4(vDr$6ypr)tXOD881QJJ*07mY^6R)=K+f`|xR_Z8V!H{bv zGmqwfMATFbgy_%YfUA^rxAw~$f6n6Xz9yiHfbvhMDf`s3PRLmD#ac`40q7PL2WsLk zPq32s{nk1f7+uGi?dt_^Xqt!GFX;KBc$;Dp4%_I%W}WS4^}aST^7N~;@`1e#?%}7U zi!xDsZc?_cA$i$DLBrDRYm$_^%0AeivMwEeRzM7>;^|rrP1Gl#yPMs!OhSH)=9(77 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�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 Bw}k)z From b2e22dc664fab0477cbd914b729f43e27e17463c Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Fri, 7 Nov 2025 12:29:38 +0100 Subject: [PATCH 36/42] Updated date --- plugin.info.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin.info.txt b/plugin.info.txt index 39c5c18..2371a85 100644 --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base botmon author Sascha Leib email ad@hominem.com -date 2025-11-05 +date 2025-11-07 name Bot Monitoring desc A tool for monitoring and analysing bot traffic to your wiki (under development) url https://www.dokuwiki.org/plugin:botmon From 6861f2af57b1dfa20bce13c2f3c5d4c048a9bdd1 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Fri, 7 Nov 2025 18:46:17 +0100 Subject: [PATCH 37/42] Highlight multi-blocked visits --- admin.css | 6 ++++-- admin.js | 18 ++++++++++-------- captcha.js | 5 ++--- img/captcha.png | Bin 4571 -> 5135 bytes 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/admin.css b/admin.css index 899d43a..8f3cad1 100644 --- a/admin.css +++ b/admin.css @@ -119,12 +119,14 @@ /* Captcha statuses */ &.captcha::before { background-image: url('img/captcha.png') } &.cap_Y::before { background-position-y: -20px } - &.cap_YN::before { background-position-y: -40px } + &.cap_YN::before, &.cap_YYN::before { background-position-y: -40px } &.cap_W::before { background-position-y: -60px } &.cap_H::before { background-position-y: -80px } &.cap_X::before { background-position-y: -100px } - &.cap_YH::before { background-position-y: -120px } + &.cap_YH::before, &.cap_YYH::before { background-position-y: -120px } &.cap_YNH::before { background-position-y: -140px } + &.cap_YY::before { background-position-y: -160px } + &.cap_N::before { background-position-y: -180px } /* Country flags */ /* Note: flag images and CSS adapted from: https://github.com/lafeber/world-flags-sprite/ */ diff --git a/admin.js b/admin.js index 204af86..896711b 100644 --- a/admin.js +++ b/admin.js @@ -416,7 +416,7 @@ BotMon.live = { _client: BotMon.live.data.clients.match(nv.agent) ?? null, // client info _platform: BotMon.live.data.platforms.match(nv.agent), // platform info _captcha: {'X': 0, 'Y': 0, 'N': 0, 'W':0, 'H': 0, - _str: function() { return (this.X > 0 ? 'X' : '') + (this.Y > 0 ? 'Y' : '') + (this.N > 0 ? 'N' : '') + (this.W > 0 ? 'W' : '') + (this.H > 0 ? 'H' : ''); } + _str: function() { return (this.X > 0 ? 'X' : '') + (this.Y > 0 ? (this.Y > 1 ? 'YY' : 'Y') : '') + (this.N > 0 ? 'N' : '') + (this.W > 0 ? 'W' : '') + (this.H > 0 ? 'H' : ''); } } // captcha counter }}; model._visitors.push(visitor); @@ -574,11 +574,13 @@ BotMon.live = { _makeCaptchaTitle: function(cObj) { const cStr = cObj._str(); switch (cStr) { - case 'Y': - case 'NY': return "Blocked."; + case 'Y': return "Blocked."; + case 'YY': return "Blocked multiple times."; case 'YN': return "Solved"; + case 'YYN': return "Solved after multiple attempts"; case 'W': return "Whitelisted"; case 'H': return "HEAD request, no captcha"; + case 'YH': case 'YYH': return "Block & HEAD mixed"; default: return "Undefined: " + cStr; } } @@ -2083,7 +2085,7 @@ BotMon.live = { botList.forEach( (botInfo) => { const bli = makeElement('dd'); bli.appendChild(makeElement('span', {'class': 'has_icon bot bot_' + botInfo.id }, botInfo.name)); - bli.appendChild(makeElement('span', {'class': 'count' }, botInfo.count)); + bli.appendChild(makeElement('span', {'class': 'count' }, botInfo.count || kNoData)); botElement.append(bli) }); } @@ -2098,7 +2100,7 @@ BotMon.live = { ispList.forEach( (netInfo) => { const li = makeElement('dd'); li.appendChild(makeElement('span', {'class': 'has_icon ipaddr ip' + netInfo.typ }, netInfo.name)); - li.appendChild(makeElement('span', {'class': 'count' }, netInfo.count)); + li.appendChild(makeElement('span', {'class': 'count' }, netInfo.count || kNoData)); botIps.append(li) }); } @@ -2111,7 +2113,7 @@ BotMon.live = { countryList.forEach( (cInfo) => { const cLi = makeElement('dd'); cLi.appendChild(makeElement('span', {'class': 'has_icon country ctry_' + cInfo.id.toLowerCase() }, cInfo.name)); - cLi.appendChild(makeElement('span', {'class': 'count' }, cInfo.count)); + cLi.appendChild(makeElement('span', {'class': 'count' }, cInfo.count || kNoData)); botCountries.appendChild(cLi); }); } @@ -2210,7 +2212,7 @@ BotMon.live = { usrCtryList.forEach( (cInfo) => { const cLi = makeElement('dd'); cLi.appendChild(makeElement('span', {'class': 'has_icon country ctry_' + cInfo.id.toLowerCase() }, cInfo.name)); - cLi.appendChild(makeElement('span', {'class': 'count' }, cInfo.count)); + cLi.appendChild(makeElement('span', {'class': 'count' }, cInfo.count || kNoData)); usrCountries.appendChild(cLi); }); } @@ -2234,7 +2236,7 @@ BotMon.live = { pgDd.appendChild(makeElement('span', { 'class': 'count', 'title': pgInfo.count + " page views" - }, pgInfo.count)); + }, pgInfo.count || kNoData)); wmpages.appendChild(pgDd); }); } diff --git a/captcha.js b/captcha.js index c818c97..4a72efe 100644 --- a/captcha.js +++ b/captcha.js @@ -176,11 +176,10 @@ const $BMCaptcha = { (new Date()).toISOString().substring(0, 10) ]; if (performance.now() - $BMCaptcha._st <= 1500) dat.push(performance.now() - $BMCaptcha._st); - const hash = $BMCaptcha.digest.hash(dat.join(';')); // set the cookie: - document.cookie = "DWConfirm=" + encodeURIComponent(hash) + '; path=/; session;' - + (document.location.protocol === 'https:' ? ' secure;' : ''); + document.cookie = "DWConfirm=" + encodeURIComponent($BMCaptcha.digest.hash(dat.join(';'))) + '; path=/; session;'; + // + (document.location.protocol === 'https:' ? ' secure;' : ''); } catch (err) { console.error(err); diff --git a/img/captcha.png b/img/captcha.png index aec43eb5fb3c02d0ce30c5e420c69f6f7128ad7f..fd841fc2006b958574123a2e992e0bbd71bb8fc1 100644 GIT binary patch delta 4879 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 BDun<5 delta 4311 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 From cb91697912a3c2642b72c3d0bc9fa6758b1e5bb4 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Wed, 26 Nov 2025 22:51:18 +0100 Subject: [PATCH 38/42] No-JS Warning Shows a warning message for users without JS active. --- action.php | 4 ++++ captcha.js | 5 +++++ config/known-ipranges.json | 2 -- lang/de/lang.php | 1 + lang/en/lang.php | 1 + lang/fr/lang.php | 9 +++++++++ style.less | 14 +++++++++++++- 7 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 lang/fr/lang.php diff --git a/action.php b/action.php index 2f27af2..c38ea2f 100644 --- a/action.php +++ b/action.php @@ -270,6 +270,10 @@ class action_plugin_botmon extends DokuWiki_Action_Plugin { echo DOKU_TAB . '};' . NL; echo '' . NL; + + // insert a warning message for users without JavaScript: + echo '

    ' . $this->getLang('bm_noJsWarning') . '

    ' . NL; + } } diff --git a/captcha.js b/captcha.js index 4a72efe..8a059e2 100644 --- a/captcha.js +++ b/captcha.js @@ -6,6 +6,11 @@ const $BMCaptcha = { init: function() { + + // hide the NoJS warning: + document.getElementById('BM__NoJSWarning').close(); + + // install the captcha: document.getElementsByTagName('body')[0].classList.add('botmon_captcha'); $BMCaptcha._cbDly = 1.5; $BMCaptcha.install() diff --git a/config/known-ipranges.json b/config/known-ipranges.json index 11449a3..3d26f0f 100644 --- a/config/known-ipranges.json +++ b/config/known-ipranges.json @@ -6,7 +6,6 @@ {"id": "brasilnet", "name": "BrasilNet"}, {"id": "charter", "name": "Charter Inc. Range"}, {"id": "chinanet", "name": "ChinaNet"}, - {"id": "cloudflare", "name": "Cloudflare Network"}, {"id": "cnisp", "name": "China ISP Range"}, {"id": "cnmob", "name": "China Mobile"}, {"id": "domtehniki", "name": "Dom Tehniki / WS Telecom"}, @@ -108,7 +107,6 @@ {"from": "2603:8000::::::", "to": "2603:80ff:ffff:ffff:ffff:ffff:ffff:ffff", "m": 24, "g": "charter"}, {"from": "2607:a400::::::", "to": "2607:a400:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "g": "zenlayer"}, {"from": "2804:::::::", "to": "2804:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "m": 16, "g": "misc_sa"}, - {"from": "2a09:bac3::::::", "to": "2a09:bac3:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32,"g": "cloudflare"}, {"from": "2a0a:4cc0::::::", "to": "2a0a:4cc0:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "g": "netcup"} ] } \ No newline at end of file diff --git a/lang/de/lang.php b/lang/de/lang.php index 1c6bd10..f84a16e 100644 --- a/lang/de/lang.php +++ b/lang/de/lang.php @@ -12,3 +12,4 @@ $lang['bm_dlgConfirm'] = 'Klicke, um zu bestätigen.'; $lang['bm_dlgChecking'] = 'Wird überprüft …'; $lang['bm_dlgLoading'] = 'Seite wird geladen …'; $lang['bm_dlgError'] = 'Es ist ein Fehler aufgetreten.'; +$lang['bm_noJsWarning'] = 'Bitte aktivieren Sie JavaScript, um diese Seite anzuzeigen.'; diff --git a/lang/en/lang.php b/lang/en/lang.php index b5b9334..d455fde 100644 --- a/lang/en/lang.php +++ b/lang/en/lang.php @@ -12,3 +12,4 @@ $lang['bm_dlgConfirm'] = 'Click to confirm.'; $lang['bm_dlgChecking'] = 'Checking …'; $lang['bm_dlgLoading'] = 'Loading page …'; $lang['bm_dlgError'] = 'An error occured.'; +$lang['bm_noJsWarning'] = 'This page requires JavaScript to be enabled.'; diff --git a/lang/fr/lang.php b/lang/fr/lang.php new file mode 100644 index 0000000..b8af382 --- /dev/null +++ b/lang/fr/lang.php @@ -0,0 +1,9 @@ + + */ + +// Captcha dialog locale strings: +$lang['bm_noJsWarning'] = 'Veuillez activer JavaScript pour afficher cette page.'; diff --git a/style.less b/style.less index 593404c..67a5325 100644 --- a/style.less +++ b/style.less @@ -106,7 +106,19 @@ body.botmon_captcha { } } -// smaller screens: +// no js warning +#BM__NoJSWarning { + position: fixed; + bottom: calc(50vh - 2.5rem); + width: 100%; max-width: fit-content; + margin: 0 auto; + padding: .25rem 1rem; + border-radius: .5rem; + border: red solid 2pt; + box-shadow: rgba(128, 0, 0, 0.5) .25rem .25rem .5rem; +} + +// captcha on smaller screens: @media (max-width: 480px) { body.botmon_captcha #botmon_captcha_box { width: 100vw; From 53287868f786eb8679a3b278eb9c63a3d0705d21 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sat, 6 Dec 2025 13:57:58 +0100 Subject: [PATCH 39/42] GUI Improvements Better display of pages per visit. --- admin.css | 50 ++++++++----- admin.js | 145 +++++++++++++++++++++++-------------- config/default-config.json | 4 +- config/known-bots.json | 8 +- style.less | 7 ++ 5 files changed, 132 insertions(+), 82 deletions(-) diff --git a/admin.css b/admin.css index 8f3cad1..f050a96 100644 --- a/admin.css +++ b/admin.css @@ -701,20 +701,9 @@ &:nth-child(odd) { background-color: #DFDFDF; } - &.detailled { + /*&.detailled { outline: red dotted 1pt; - } - div.row { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: baseline; - white-space: nowrap; - line-height: 1.35em; - } - span { - display: inline-block; - } + }*/ } } a[hreflang] { @@ -733,6 +722,23 @@ padding: 0 1pt; margin-left: .2em; } + div.row { + & { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: baseline; + white-space: nowrap; + line-height: 1.35em; + } + & > div { + display: inline-flex; + column-gap: .4em; + } + } + span { + display: inline-block; + } span.first-seen { min-width: 4.2em; text-align: right; @@ -741,20 +747,26 @@ font-size: smaller; } span.bounce { - width: 1.25em; height: 1.25em; + width: 1em; height: 1em; overflow: hidden; } span.bounce::before { display: inline-block; content: ''; - width: 1.25em; height: 1em; + width: 1em; height: 1em; background: transparent url('img/bounce.svg') center no-repeat; background-size: .95em; } - span.referer { + span.referer, span.views, span.ip-address, span.user-agent { font-size: smaller; + } + span.referer, span.ip-address, span.user-agent { margin-left: .67rem; } + span.user-agent { + line-break: anywhere; + text-wrap-mode: wrap; + } } } @@ -779,7 +791,7 @@ } /* pages seen */ - span.pageseen, span.pageviews { + span.pageseen/*, span.pageviews */{ border: #999 solid 1px; padding: 0 2px; font-size: smaller; @@ -794,13 +806,13 @@ background: transparent url('img/page.svg') center no-repeat; background-size: 1.25em; } - span.pageviews::before { + /*span.pageviews::before { content : ''; display: inline-block; width: 1.25em; height: 1.25em; background: transparent url('img/views.svg') center no-repeat; background-size: 1.25em; - } + }*/ } /* item footer */ diff --git a/admin.js b/admin.js index 896711b..fc3cbc1 100644 --- a/admin.js +++ b/admin.js @@ -105,7 +105,7 @@ const BotMon = { r.textContent = text.toString(); } } catch(e) { - console.error(e); + console.error('Botmon:', e); } return r; }, @@ -399,7 +399,7 @@ BotMon.live = { nv._country = countryName.of(nv.geo.substring(0,2)) ?? nv.geo; } } catch (err) { - console.error(err); + console.error('Botmon:', err); nv._country = 'Error'; } @@ -445,6 +445,8 @@ BotMon.live = { prereg = model._makePageView(nv, type); visitor._pageViews.push(prereg); } + prereg._loadCount += 1; + prereg._viewCount += (nv.captcha == 'Y' ? 0 : 1); // update last seen date prereg._lastSeen = nv.ts; @@ -517,7 +519,7 @@ BotMon.live = { // find the visit info: let visitor = model.findVisitor(dat, type); if (!visitor) { - console.info(`No visitor with ID “${dat.id}” found, registering as a new one.`); + console.info(`Botmon: No visitor with ID “${dat.id}” found, registering as a new one.`); visitor = model.registerVisit(dat, type, true); } if (visitor) { @@ -528,7 +530,7 @@ BotMon.live = { // get the page view info: let pv = model._getPageView(visitor, dat); if (!pv) { - console.info(`No page view for visit ID “${dat.id}”, page “${dat.pg}”, registering a new one.`); + console.info(`Botmon: No page view for visit ID “${dat.id}”, page “${dat.pg}”, registering a new one.`); pv = model._makePageView(dat, type); visitor._pageViews.push(pv); } @@ -543,15 +545,14 @@ BotMon.live = { // helper function to create a new "page view" item: _makePageView: function(data, type) { - // console.info('_makePageView', data); + //console.info('_makePageView', data); // try to parse the referrer: let rUrl = null; try { rUrl = ( data.ref && data.ref !== '' ? new URL(data.ref) : null ); } catch (e) { - console.warn(`Invalid referer: “${data.ref}”.`); - console.info(data); + console.info(`Botmon: Ignoring invalid referer: “${data.ref}”.`); } return { @@ -564,9 +565,11 @@ BotMon.live = { _lastSeen: data.ts, _seenBy: [type], _jsClient: ( type !== BM_LOGTYPE.SERVER), + _agent: data.agent, _viewCount: 0, _loadCount: 0, - _tickCount: 0 + _tickCount: 0, + _captcha: data.captcha }; }, @@ -1025,7 +1028,7 @@ BotMon.live = { arr = me._countries.bot; break; default: - console.warn(`Unknown user type ${type} in function addToCountries.`); + console.warn(`Botmon: Unknown user type ${type} in function addToCountries.`); } if (arr) { @@ -1065,7 +1068,7 @@ BotMon.live = { arr = me._countries.bot; break; default: - console.warn(`Unknown user type ${type} in function getCountryList.`); + console.warn(`Botmon: Unknown user type ${type} in function getCountryList.`); return; } @@ -1652,7 +1655,7 @@ BotMon.live = { const pId = ( visitor._platform ? visitor._platform.id : ''); - if (visitor._platform.id == null) console.log(visitor._platform); + //if (visitor._platform.id == null) console.log(visitor._platform); return platforms.includes(pId); }, @@ -1766,8 +1769,6 @@ BotMon.live = { totalTime += (pvArr[i] - pvArr[i-1]); } - //console.log(' ', totalTime , Math.round(totalTime / (pvArr.length * 1000)), (( totalTime / pvArr.length ) <= maxTime * 1000), visitor.ip); - return (( totalTime / pvArr.length ) <= maxTime * 1000); } }, @@ -1868,7 +1869,7 @@ BotMon.live = { columns = ['ts','ip','pg','id','agent']; break; default: - console.warn(`Unknown log type ${type}.`); + console.warn(`Botmon: Unknown log type ${type} in function “loadLogFile” (1).`); return; } @@ -1924,7 +1925,7 @@ BotMon.live = { BotMon.live.data.model.updateTicks(data); break; default: - console.warn(`Unknown log type ${type}.`); + console.warn(`Botmon: Unknown log type ${type} in function “loadLogFile” (2).`); return; } }); @@ -2068,7 +2069,7 @@ BotMon.live = { value = BotMon.t._getRatio(data.views.suspected + data.views.bots, data.views.users + data.views.humans, 100); break; default: - console.warn(`Unknown list type ${i}.`); + console.warn(`Botmon: Unknown list type ${i} in function “overview.make” (1).`); } dd.appendChild(makeElement('span', {}, title)); dd.appendChild(makeElement('strong', {}, value)); @@ -2096,7 +2097,6 @@ BotMon.live = { botIps.appendChild(makeElement('dt', {}, "Top bot Networks")); const ispList = BotMon.live.data.analytics.getTopBotISPs(5); - //console.log(ispList); ispList.forEach( (netInfo) => { const li = makeElement('dd'); li.appendChild(makeElement('span', {'class': 'has_icon ipaddr ip' + netInfo.typ }, netInfo.name)); @@ -2156,7 +2156,7 @@ BotMon.live = { value = bounceRate + '%'; break; default: - console.warn(`Unknown list type ${i}.`); + console.warn(`Botmon: Unknown list type ${i} in function “overview.make” (2).`); } dd.appendChild(makeElement('span', {}, title)); dd.appendChild(makeElement('strong', {}, value)); @@ -2297,7 +2297,7 @@ BotMon.live = { value = (data.captcha.humans_blocked / data.visits.humans * 100).toFixed(0) + '%'; break; default: - console.warn(`Unknown list type ${i}.`); + console.warn(`Botmon: Unknown list type ${i} in function “overview.make” (3).`); } dd.appendChild(makeElement('span', {}, title)); dd.appendChild(makeElement('strong', {}, value || kNoData)); @@ -2337,7 +2337,7 @@ BotMon.live = { value = (data.captcha.sus_blocked / data.visits.suspected * 100).toFixed(0) + '%'; break; default: - console.warn(`Unknown list type ${i}.`); + console.warn(`Botmon: Unknown list type ${i} in function “BotMon.live.gui.overview.make” (4).`); } dd.appendChild(makeElement('span', {}, title)); dd.appendChild(makeElement('strong', {}, value || kNoData)); @@ -2377,7 +2377,7 @@ BotMon.live = { value = (data.captcha.bots_blocked / data.visits.bots * 100).toFixed(0) + '%'; break; default: - console.warn(`Unknown list type ${i}.`); + console.warn(`Botmon: Unknown list type ${i} in function “BotMon.live.gui.overview.make” (5).`); } dd.appendChild(makeElement('span', {}, title)); dd.appendChild(makeElement('strong', {}, value || kNoData)); @@ -2405,7 +2405,7 @@ BotMon.live = { }, setError: function(txt) { - console.error(txt); + console.error('Botmon:', txt); BotMon.live.gui.status._errorCount += 1; const el = document.getElementById('botmon__today__status'); if (el) { @@ -2470,7 +2470,7 @@ BotMon.live = { infolink = 'https://leib.be/sascha/projects/dokuwiki/botmon/info/known_bots'; break; default: - console.warn('Unknown list number.'); + console.warn(`Botmon: Unknown list number. ${i} in function “lists.init”.`); } const details = makeElement('details', { @@ -2737,13 +2737,14 @@ BotMon.live = { } dl.appendChild(make('dt', {}, "Actions:")); + dl.appendChild(make('dd', {'class': 'views'}, "Page loads: " + data._loadCount.toString() + ( data._captcha['Y'] > 0 ? ", captchas: " + data._captcha['Y'].toString() : '') + ", views: " + data._viewCount.toString() )); - if (data.ref && data.ref !== '') { + if (!combinedItem && data.ref && data.ref !== '') { dl.appendChild(make('dt', {}, "Referrer:")); const refInfo = BotMon.live.data.analytics.getRefererInfo(data.ref); @@ -2824,19 +2825,13 @@ BotMon.live = { } } - // for debugging only. Disable on production: - /*dl.appendChild(make('dt', {}, "Debug info:")); - const dbgDd = make('dd', {'class': 'debug'}); - dbgDd.innerHTML = '
    ' + JSON.stringify(data, null, 4) + '
    '; - dl.appendChild(dbgDd);*/ - // return the element to add to the UI: return dl; }, // make a page view item: _makePageViewItem: function(page, moreInfo) { - console.log("makePageViewItem:",page); + //console.log("makePageViewItem:",page); // shortcut for neater code: const make = BotMon.t._makeElement; @@ -2856,16 +2851,15 @@ BotMon.live = { const rightGroup = row1.appendChild(make('div')); // right-hand group - // get the time difference: rightGroup.appendChild(make('span', { 'class': 'first-seen', 'title': "First visited: " + page._firstSeen.toLocaleString() + " UTC" }, BotMon.t._formatTime(page._firstSeen))); - - rightGroup.appendChild(make('span', { /* page loads */ - 'class': 'has_icon pageviews', - 'title': page._viewCount.toString() + " page load(s)" - }, page._viewCount.toString())); + + + rightGroup.appendChild(make('span', { // captcha status + 'class': 'icon_only captcha cap_' + page._captcha, + }, page._captcha)); pgLi.appendChild(row1); @@ -2876,31 +2870,74 @@ BotMon.live = { // page referrer: if (page._ref) { row2.appendChild(make('span', { - 'class': 'referer', - 'title': "Referrer: " + page._ref.href - }, page._ref.hostname)); + 'class': 'referer' + }, "Referrer: " + page._ref.hostname)); } else { row2.appendChild(make('span', { 'class': 'referer' }, "No referer")); } - // visit duration: - let visitTimeStr = "Bounce"; - const visitDuration = page._lastSeen.getTime() - page._firstSeen.getTime(); - if (visitDuration > 0) { - visitTimeStr = Math.floor(visitDuration / 1000) + "s"; - } - const tDiff = BotMon.t._formatTimeDiff(page._firstSeen, page._lastSeen); - if (tDiff) { - row2.appendChild(make('span', {'class': 'visit-length', 'title': 'Last seen: ' + page._lastSeen.toLocaleString()}, tDiff)); - } else { - row2.appendChild(make('span', { - 'class': 'bounce', - 'title': "Visitor bounced"}, "Bounce")); + const rightGroup2 = row2.appendChild(make('div')); // right-hand group + + // visit duration: + let visitTimeStr = "Bounce"; + const visitDuration = page._lastSeen.getTime() - page._firstSeen.getTime(); + if (visitDuration > 0) { + visitTimeStr = Math.floor(visitDuration / 1000) + "s"; + } + var tDiff = BotMon.t._formatTimeDiff(page._firstSeen, page._lastSeen); + if (tDiff) { + tDiff += " (" + page._tickCount.toString() + " ticks)"; + rightGroup2.appendChild(make('span', { + 'class': 'visit-length', + 'title': "Last seen: " + page._lastSeen.toLocaleString()}, + tDiff)); + } else { + rightGroup2.appendChild(make('span', { + 'class': 'bounce', + 'title': "Visitor bounced (no ticks)"}, + "Bounce")); + } + + pgLi.appendChild(row2); + + if (moreInfo) { /* LINE 3 */ + + const row3 = make('div', {'class': 'row'}); + + const leftGroup3 = row3.appendChild(make('div')); // left-hand group + + leftGroup3.appendChild(make('span', { + 'class': 'ip-address' + }, "IP: " + page.ip + ': ')); + + const rightGroup3 = row3.appendChild(make('div')); // right-hand group + + rightGroup3.appendChild(make('span', { + 'class': 'views' + }, page._loadCount.toString() + " loads, " + page._viewCount.toString() + " views")); + + + pgLi.appendChild(row3); + + /* LINE 4 */ + + if (page._agent) { + const row4 = make('div', {'class': 'row'}); + row4.appendChild(make('span', { + 'class': 'user-agent' + }, "User-agent: " + page._agent)); + pgLi.appendChild(row4); } - pgLi.appendChild(row2); + /* LINE X (DEBUG ONLY!) + + const rowx = make('div', {'class': 'row'}); + rowx.appendChild(make('pre', {'style': 'white-space: normal;width:calc(100% - 24rem)'}, JSON.stringify(page))); + + pgLi.appendChild(rowx); */ + } return pgLi; } diff --git a/config/default-config.json b/config/default-config.json index 01e3a74..0591c77 100644 --- a/config/default-config.json +++ b/config/default-config.json @@ -75,11 +75,11 @@ }, {"func": "blockedByCaptcha", "params": [], "id": "blockedByCaptcha", "desc": "Visitor did not solve the captcha", - "bot": 20 + "bot": 50 }, {"func": "whitelistedByCaptcha", "params": [], "id": "whitelistedByCaptcha", "desc": "Visitor uses a whitelisted IP address", - "bot": -20 + "bot": -30 } ] } \ No newline at end of file diff --git a/config/known-bots.json b/config/known-bots.json index 31ec325..a733339 100644 --- a/config/known-bots.json +++ b/config/known-bots.json @@ -7,7 +7,7 @@ {"id": "googlebot", "n": "GoogleBot", "r": ["Googlebot"], - "rx": ["Googlebot\\/(\\d+\\.\\d+)", "Googlebot-Image\\/(\\d+\\.\\d+)"], + "rx": ["Googlebot\\/(\\d+\\.\\d+)", "Googlebot-Image\\/(\\d+\\.\\d+)","\\sGoogleOther(\\-\\w+)?[\\)\\/]"], "url": "http://www.google.com/bot.html" }, {"id": "googleads", @@ -22,12 +22,6 @@ "rx": ["APIs-Google"], "url": "https://developers.google.com/search/docs/crawling-indexing/google-special-case-crawlers" }, - {"id": "googleother", - "n": "GoogleOther", - "r": ["GoogleOther"], - "rx": ["\\sGoogleOther(\\-\\w+)?[\\)\\/]"], - "url": "https://developers.google.com/search/docs/crawling-indexing/google-common-crawlers#googleother" - }, {"id": "googinspct", "n": "Google-InspectionTool", "r": ["Google-InspectionTool"], diff --git a/style.less b/style.less index 67a5325..9314c05 100644 --- a/style.less +++ b/style.less @@ -106,6 +106,12 @@ body.botmon_captcha { } } +@keyframes delayed-fade-in { + 0% { opacity: 0; } + 50% { opacity: 0; } + 100% { opacity: 1; } +} + // no js warning #BM__NoJSWarning { position: fixed; @@ -116,6 +122,7 @@ body.botmon_captcha { border-radius: .5rem; border: red solid 2pt; box-shadow: rgba(128, 0, 0, 0.5) .25rem .25rem .5rem; + animation: delayed-fade-in 4s forwards; } // captcha on smaller screens: From da16b8c82f3a47fd22717ae24d3104903294c469 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sat, 6 Dec 2025 18:20:58 +0100 Subject: [PATCH 40/42] Visited pages details --- admin.css | 5 ++--- admin.js | 45 +++++++++++++++++---------------------------- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/admin.css b/admin.css index f050a96..7420fcc 100644 --- a/admin.css +++ b/admin.css @@ -119,14 +119,13 @@ /* Captcha statuses */ &.captcha::before { background-image: url('img/captcha.png') } &.cap_Y::before { background-position-y: -20px } - &.cap_YN::before, &.cap_YYN::before { background-position-y: -40px } + &.cap_N::before, &.cap_YN::before, &.cap_YYN::before { background-position-y: -40px } &.cap_W::before { background-position-y: -60px } &.cap_H::before { background-position-y: -80px } - &.cap_X::before { background-position-y: -100px } + &.cap_X::before, &.cap_undefined::before { background-position-y: -100px } &.cap_YH::before, &.cap_YYH::before { background-position-y: -120px } &.cap_YNH::before { background-position-y: -140px } &.cap_YY::before { background-position-y: -160px } - &.cap_N::before { background-position-y: -180px } /* Country flags */ /* Note: flag images and CSS adapted from: https://github.com/lafeber/world-flags-sprite/ */ diff --git a/admin.js b/admin.js index fc3cbc1..a55f754 100644 --- a/admin.js +++ b/admin.js @@ -2523,6 +2523,7 @@ BotMon.live = { }, _makeVisitorItem: function(data, type) { + //console.info('BotMon.live.gui.lists._makeVisitorItem()', data, type); // shortcuts for neater code: const make = BotMon.t._makeElement; @@ -2538,7 +2539,7 @@ BotMon.live = { // combine with other networks? const combineNets = (BMSettings.hasOwnProperty('combineNets') ? BMSettings['combineNets'] : true) - && data.hasOwnProperty('_ipRange'); + && data.hasOwnProperty('_ipRange'); const li = make('li'); // root list item const details = make('details'); @@ -2549,18 +2550,6 @@ BotMon.live = { const span1 = make('span'); /* left-hand group */ - /*if (data._type !== BM_USERTYPE.KNOWN_BOT) { // No platform/client for bots // disabled because no longer relevant - span1.appendChild(make('span', { // Platform - 'class': 'icon_only platform pf_' + (data._platform ? data._platform.id : 'unknown'), - 'title': "Platform: " + platformName - }, platformName)); - - span1.appendChild(make('span', { // Client - 'class': 'icon_only client client cl_' + (data._client ? data._client.id : 'unknown'), - 'title': "Client: " + clientName - }, clientName)); - }*/ - // identifier: if (data._type == BM_USERTYPE.KNOWN_BOT) { /* Bot only */ @@ -2602,7 +2591,6 @@ BotMon.live = { 'title': data._pageViews.length + " page load(s)" }, data._pageViews.length)); - // referer icons: if ((data._type == BM_USERTYPE.PROBABLY_HUMAN || data._type == BM_USERTYPE.LIKELY_BOT) && data.ref) { const refInfo = BotMon.live.data.analytics.getRefererInfo(data.ref); @@ -2615,15 +2603,13 @@ BotMon.live = { summary.appendChild(span1); const span2 = make('span'); /* right-hand group */ - // country flag: - if (!combineNets) { // not for combined networks - if (data.geo && data.geo !== 'ZZ') { - span2.appendChild(make('span', { - 'class': 'icon_only country ctry_' + data.geo.toLowerCase(), - 'data-ctry': data.geo, - 'title': "Country: " + ( data._country || "Unknown") - }, ( data._country || "Unknown") )); - } + // country flag (not for combined networks): + if (!combineNets && data.geo && data.geo !== 'ZZ') { + span2.appendChild(make('span', { + 'class': 'icon_only country ctry_' + data.geo.toLowerCase(), + 'data-ctry': data.geo, + 'title': "Country: " + ( data._country || "Unknown") + }, ( data._country || "Unknown") )); } span2.appendChild(make('span', { // seen-by icon: @@ -2660,7 +2646,7 @@ BotMon.live = { if (data.ip == '127.0.0.1' || data.ip == '::1' ) ipType = '0'; const platformName = (data._platform ? data._platform.n : 'Unknown'); const clientName = (data._client ? data._client.n: 'Unknown'); - const combinedItem = data.hasOwnProperty('_ipRange'); + const combinedItem = type == 'knownBots' || data.hasOwnProperty('_ipRange'); const dl = make('dl', {'class': 'visitor_details'}); @@ -2680,6 +2666,9 @@ BotMon.live = { } + dl.appendChild(make('dt', {}, "User-Agent:")); + dl.appendChild(make('dd', {'class': 'agent'}, data.agent)); + } else if (!combinedItem) { /* not for bots or combined items */ dl.appendChild(make('dt', {}, "Client:")); /* client */ @@ -2774,7 +2763,7 @@ BotMon.live = { /* list all page views */ data._pageViews.sort( (a, b) => a._firstSeen - b._firstSeen ); data._pageViews.forEach( (page) => { - pageList.appendChild(BotMon.live.gui.lists._makePageViewItem(page, combinedItem)); + pageList.appendChild(BotMon.live.gui.lists._makePageViewItem(page, combinedItem, type)); }); pagesDd.appendChild(pageList); dl.appendChild(pagesDd); @@ -2830,7 +2819,7 @@ BotMon.live = { }, // make a page view item: - _makePageViewItem: function(page, moreInfo) { + _makePageViewItem: function(page, moreInfo, type) { //console.log("makePageViewItem:",page); // shortcut for neater code: @@ -2910,7 +2899,7 @@ BotMon.live = { leftGroup3.appendChild(make('span', { 'class': 'ip-address' - }, "IP: " + page.ip + ': ')); + }, "IP: " + page.ip)); const rightGroup3 = row3.appendChild(make('div')); // right-hand group @@ -2923,7 +2912,7 @@ BotMon.live = { /* LINE 4 */ - if (page._agent) { + if (type !== 'knownBots' && page._agent) { const row4 = make('div', {'class': 'row'}); row4.appendChild(make('span', { 'class': 'user-agent' From 2651a7d446507631c9e2083a10865cc8c141e844 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sat, 6 Dec 2025 18:28:23 +0100 Subject: [PATCH 41/42] Cleaned SA ranges Removed Brazilnet and other South-American ISPs. --- config/known-ipranges.json | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/config/known-ipranges.json b/config/known-ipranges.json index 3d26f0f..11b1989 100644 --- a/config/known-ipranges.json +++ b/config/known-ipranges.json @@ -3,7 +3,6 @@ {"id": "alibaba", "name": "Alibaba Network"}, {"id": "amazon", "name": "Amazon Data Centres"}, {"id": "bezeq", "name": "Bezeq Int."}, - {"id": "brasilnet", "name": "BrasilNet"}, {"id": "charter", "name": "Charter Inc. Range"}, {"id": "chinanet", "name": "ChinaNet"}, {"id": "cnisp", "name": "China ISP Range"}, @@ -12,7 +11,7 @@ {"id": "google", "name": "Google LLC Network"}, {"id": "hetzner", "name": "Hetzner US"}, {"id": "huawei", "name": "Huawei Network"}, - {"id": "misc_sa", "name": "Misc. SA ISPs"}, + {"id": "netcup", "name": "netcup GmbH"}, {"id": "tencent", "name": "Tencent Network"}, {"id": "unicom", "name": "China Unicom"}, {"id": "vnpt", "name": "Vietnam Telecom"}, @@ -68,8 +67,6 @@ {"from": "123.16.0.0", "to": "123.31.255.254", "m": 12, "g": "vnpt"}, {"from": "124.243.128.0", "to": "124.243.191.254", "m": 18, "g": "huawei"}, {"from": "136.107.0.0", "to": "136.125.255.254", "m": "+", "g": "google"}, - {"from": "138.59.0.0", "to": "138.59.225.254", "m": 16, "g": "misc_sa"}, - {"from": "138.121.0.0", "to": "138.121.225.254", "m": 16, "g": "misc_sa"}, {"from": "142.147.128.0", "to": "1142.147.255.254", "m": 17, "g": "w2obj"}, {"from": "146.174.128.0", "to": "146.174.191.254", "m": 18, "g": "huawei"}, {"from": "149.126.192.0", "to": "149.126.223.255", "m": 19, "g": "domtehniki"}, @@ -78,25 +75,10 @@ {"from": "159.138.0.0", "to": "159.138.225.254", "m": 16, "g": "huawei"}, {"from": "162.128.0.0", "to": "162.128.255.254", "m": 16, "g": "zenlayer"}, {"from": "166.108.192.0", "to": "166.108.255.254", "m": 18, "g": "huawei"}, - {"from": "168.232.192.0", "to": "168.232.255.254", "m": 16, "g": "misc_sa"}, - {"from": "170.0.0.0", "to": "170.3.255.254", "m": 14, "g": "misc_sa"}, - {"from": "170.80.0.0", "to": "170.83.255.254", "m": 14, "g": "misc_sa"}, - {"from": "170.254.0.0", "to": "170.254.255.254", "m": 16, "g": "misc_sa"}, {"from": "171.224.0.0", "to": "171.239.255.254", "m": 12, "g": "viettel"}, - {"from": "177.0.0.0", "to": "177.255.255.254", "m": 8, "g": "brasilnet"}, {"from": "178.156.128.0", "to": "178.156.255.254", "m": 17, "g": "hetzner"}, - {"from": "179.0.0.0", "to": "179.255.255.254", "m": 8, "g": "brasilnet"}, - {"from": "181.0.0.0", "to": "181.255.255.254", "m": 8, "g": "misc_sa"}, {"from": "183.87.32.0", "to": "183.87.63.254", "m": 19, "g": "huawei"}, - {"from": "186.0.0.0", "to": "186.255.255.254", "m": 8, "g": "misc_sa"}, - {"from": "187.0.0.0", "to": "187.255.255.254", "m": 8, "g": "misc_sa"}, - {"from": "188.0.0.0", "to": "188.255.255.254", "m": 8, "g": "misc_sa"}, - {"from": "189.0.0.0", "to": "189.255.255.254", "m": 8, "g": "misc_sa"}, - {"from": "190.0.0.0", "to": "190.255.255.254", "m": 8, "g": "misc_sa"}, - {"from": "191.0.0.0", "to": "191.255.255.254", "m": 8, "g": "misc_sa"}, {"from": "192.124.170.0", "to": "192.124.182.254", "g": "relcom"}, - {"from": "200.0.0.0", "to": "200.255.255.254", "m": 8, "g": "misc_sa"}, - {"from": "201.0.0.0", "to": "201.255.255.254", "m": 8, "g": "misc_sa"}, {"from": "212.95.128.0", "to": "212.95.159.254", "m": 19, "g": "asiacell"}, {"from": "222.252.0.0", "to": "222.252.255.254", "m": 14, "g": "vnpt"}, {"from": "2001:4860::::::", "to": "2001:4860:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "g": "google"}, @@ -106,7 +88,6 @@ {"from": "2603:6010::::::", "to": "2603:6010:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "g": "charter"}, {"from": "2603:8000::::::", "to": "2603:80ff:ffff:ffff:ffff:ffff:ffff:ffff", "m": 24, "g": "charter"}, {"from": "2607:a400::::::", "to": "2607:a400:ffff:ffff:ffff:ffff:ffff:ffff", "m": 32, "g": "zenlayer"}, - {"from": "2804:::::::", "to": "2804:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "m": 16, "g": "misc_sa"}, {"from": "2a0a:4cc0::::::", "to": "2a0a:4cc0:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "g": "netcup"} ] } \ No newline at end of file From 259b3f4acf207589826a29fdbc43a917e24bde98 Mon Sep 17 00:00:00 2001 From: Sascha Leib Date: Sat, 6 Dec 2025 19:04:57 +0100 Subject: [PATCH 42/42] Ready to merge --- admin.js | 6 +++++- plugin.info.txt | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/admin.js b/admin.js index a55f754..122175d 100644 --- a/admin.js +++ b/admin.js @@ -504,7 +504,11 @@ BotMon.live = { prereg = model._makePageView(dat, type); visitor._pageViews.push(prereg); } + // update the page view: prereg._tickCount += 1; + if (dat.captcha) { + prereg._captcha += dat.captcha; + } }, // updating visit data from the ticker log: @@ -569,7 +573,7 @@ BotMon.live = { _viewCount: 0, _loadCount: 0, _tickCount: 0, - _captcha: data.captcha + _captcha: data.captcha ? data.captcha : 'X' }; }, diff --git a/plugin.info.txt b/plugin.info.txt index 2371a85..47c4633 100644 --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base botmon author Sascha Leib email ad@hominem.com -date 2025-11-07 +date 2025-12-06 name Bot Monitoring -desc A tool for monitoring and analysing bot traffic to your wiki (under development) +desc A tool for monitoring and blocking bot traffic to your wiki (under development) url https://www.dokuwiki.org/plugin:botmon