Telegram bot a Habr. cikkeinek személyre szabott válogatásához

Olyan kérdésekre, mint a „miért?” van egy régebbi cikk - Natural Geektimes – tisztábbá teszi a teret.

Nagyon sok cikk van, szubjektív okokból néhányat nem szeretek, néhányat pedig éppen ellenkezőleg, kár kihagyni. Szeretném optimalizálni ezt a folyamatot és időt takarítani.

A fenti cikk egy böngészőn belüli szkriptelési megközelítést javasolt, de ez nem igazán tetszett (annak ellenére, hogy korábban használtam), a következő okok miatt:

  • A számítógépen/telefonon lévő különböző böngészők esetén újra kell konfigurálnia, ha lehetséges.
  • A szerzők általi szigorú szűrés nem mindig kényelmes.
  • Nem oldódott meg a probléma azokkal a szerzőkkel, akiknek a cikkeit nem akarja kihagyni, még akkor sem, ha évente egyszer megjelennek.

Az oldalba épített szűrés a cikkértékelések alapján nem mindig kényelmes, hiszen az erősen specializált cikkek értékük ellenére meglehetősen szerény értékelést kaphatnak.

Kezdetben RSS feedet akartam generálni (vagy akár többet is), csak érdekességeket hagyva ott. De végül kiderült, hogy az RSS olvasása nem tűnik túl kényelmesnek: mindenesetre egy cikk megjegyzéséhez/szavazásához/kedvencekhez adásához át kell menni a böngészőn. Ezért írtam egy táviratbotot, amely érdekes cikkeket küld nekem személyes üzenetben. Maga a Telegram gyönyörű előzeteseket készít belőlük, ami a szerzőről/értékelésről/nézettségről szóló információkkal kombinálva meglehetősen informatívnak tűnik.

Telegram bot a Habr. cikkeinek személyre szabott válogatásához

A vágás alatt olyan részletek találhatók, mint a mű jellemzői, az írási folyamat és a technikai megoldások.

Röviden a botról

Adattár: https://github.com/Kright/habrahabr_reader

Bot a táviratban: https://t.me/HabraFilterBot

A felhasználó további besorolást állít be a címkék és a szerzők számára. Ezt követően a cikkekre egy szűrő kerül alkalmazásra - a cikk Habré-értékelése, a szerző felhasználói értékelése és a címkénkénti felhasználói értékelések átlaga összeadódik. Ha az összeg nagyobb, mint egy felhasználó által megadott küszöb, akkor a cikk átmegy a szűrőn.

A botírás mellékcélja a szórakozás és a tapasztalatszerzés volt. Ráadásul rendszeresen emlékeztettem magam arra Nem vagyok Google, és ezért sok minden a lehető legegyszerűbben és még primitívebben történik. Ez azonban nem akadályozta meg, hogy a bot megírása három hónapig tartson.

Kint nyár volt

Július véget ért, és úgy döntöttem, írok egy botot. És nem egyedül, hanem egy barátommal, aki a scala-t mesterkélt, és írni akart rá valamit. Az eleje ígéretesnek tűnt - a kódot egy csapat fogja vágni, a feladat könnyűnek tűnt, és azt hittem, pár hét vagy egy hónap múlva kész lesz a bot.

Annak ellenére, hogy az elmúlt években magam is időről időre írok kódot a sziklára, ezt a kódot általában senki sem látja vagy nézi: kisállat projektek, ötletek tesztelése, adatok előfeldolgozása, néhány koncepció elsajátítása az FP-ből. Nagyon érdekelt, hogy hogyan is néz ki a kód írása egy csapatban, mert a sziklán kódot nagyon sokféleképpen lehet írni.

Mi mehetett így? Azonban ne siessük el a dolgokat.
Minden, ami történik, nyomon követhető a véglegesítési előzmények segítségével.

Egy ismerősöm július 27-én létrehozott egy repository-t, de nem csinált mást, ezért elkezdtem kódot írni.

30 július

Röviden: Habr rss feedjének elemzését írtam.

  • com.github.pureconfig Typeafe konfigurációk közvetlen esetosztályokba történő olvasásához (nagyon kényelmesnek bizonyult)
  • scala-xml xml olvasásához: mivel kezdetben saját implementációt akartam írni az rss feedhez, és az rss feed xml formátumú, ezért ezt a könyvtárat használtam az elemzéshez. Valójában az RSS-elemzés is megjelent.
  • scalatest tesztekhez. A tesztek írása még apró projekteknél is időt takarít meg – például az xml-elemzés hibakeresésekor sokkal egyszerűbb letölteni egy fájlba, teszteket írni és hibákat kijavítani. Amikor később megjelent egy hiba valami furcsa, érvénytelen utf-8 karaktereket tartalmazó html elemzésekor, kényelmesebbnek bizonyult egy fájlba tenni, és hozzáadni egy tesztet.
  • Akka színészei. Objektíven nézve egyáltalán nem volt rájuk szükség, de a projekt poénból íródott, ki akartam próbálni őket. Ennek eredményeként készen állok elmondani, hogy tetszett. Az OOP ötlete a másik oldalról is szemlélhető - vannak szereplők, akik üzenetet váltanak. Ami még érdekesebb, hogy lehet (és kell) úgy kódot írni, hogy az üzenet ne érkezzen meg, vagy ne kerüljön feldolgozásra (általában, ha a fiók egyetlen számítógépen fut, az üzenetek nem veszhetnek el). Eleinte kapkodtam a fejem, és volt egy szemét a kódban az egymásra feliratkozó színészekkel, de végül sikerült egy meglehetősen egyszerű és elegáns architektúrát kitalálnom. Az egyes szereplőkben lévő kód egyszálúnak tekinthető, amikor egy színész összeomlik, az acca újraindítja - az eredmény egy meglehetősen hibatűrő rendszer.

9 augusztus

- tettem hozzá a projekthez scala-scrapper a Habr-ból származó html oldalak elemzéséhez (olyan információk kinyeréséhez, mint a cikk értékelése, a könyvjelzők száma stb.).

És Macskák. Akik a sziklában vannak.

Telegram bot a Habr. cikkeinek személyre szabott válogatásához

Aztán elolvastam egy könyvet az elosztott adatbázisokról, és tetszett a CRDT (konfliktusmentes replikált adattípus, https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type, habr), ezért közzétettem egy kommutatív félcsoport típusosztályát, hogy tájékozódjak a Habréról szóló cikkről.

Valójában az ötlet nagyon egyszerű – vannak monoton változó számlálóink. Az akciók száma fokozatosan nő, ahogy a pluszok száma is (valamint a mínuszok száma). Ha kétféle információval rendelkezem egy cikkről, akkor „egybe tudom egyesíteni” – a nagyobb számláló állapota számít relevánsabbnak.

A félcsoport azt jelenti, hogy egy cikkre vonatkozó információkat tartalmazó két objektum egyesíthető egybe. A kommutatív azt jelenti, hogy az A + B és a B + A is összevonható, az eredmény nem függ a sorrendtől, és végül a legújabb verzió marad. Egyébként itt is van asszociativitás.

Például a terveknek megfelelően az rss elemzés után kissé gyengített információkat adott a cikkről – olyan mutatók nélkül, mint a megtekintések száma. Ezután egy különleges színész információt szerzett a cikkekről, és a html oldalakra futott, hogy frissítse és egyesítse a régi verzióval.

Általánosságban elmondható, hogy az akka-hoz hasonlóan erre nem volt szükség, egyszerűen el lehetett tárolni az updateDate-et a cikkhez, és összevonás nélkül venni egy újabbat, de a kalandos út vezetett.

12 augusztus

Kezdtem szabadabbnak érezni magam, és szórakozásból minden chat-et külön szereplővé tettem. Elméletileg egy színész maga körülbelül 300 bájtot nyom, és milliókat lehet létrehozni, szóval ez egy teljesen normális megközelítés. Számomra úgy tűnik, hogy a megoldás elég érdekesnek bizonyult:

Az egyik szereplő hidat képezett a táviratszerver és az akkai üzenetrendszer között. Egyszerűen üzeneteket kapott, és elküldte a kívánt chat-szereplőnek. A chat-színész visszaküldhet valamit válaszul – és azt visszaküldik a táviratnak. Ami nagyon kényelmes volt, az az, hogy ez a színész a lehető legegyszerűbbnek bizonyult, és csak az üzenetekre való válaszadás logikáját tartalmazza. Egyébként minden chaten érkeztek információk az új cikkekről, de ebben megint nem látok semmilyen problémát.

Általánosságban elmondható, hogy a bot már működött, válaszolt az üzenetekre, tárolta a felhasználónak küldött cikkek listáját, és már arra gondoltam, hogy a bot majdnem készen áll. Lassan hozzáadtam olyan apró funkciókat, mint a szerzők nevének és címkéinek normalizálása (az „sd f” helyett az „s_d_f”).

Már csak egy dolog maradt kicsi, de — az államot sehol sem sikerült megmenteni.

Minden balul ütött ki

Talán észrevetted, hogy a botot többnyire egyedül írtam. Így a második résztvevő bekapcsolódott a fejlesztésbe, és a következő változások jelentek meg a kódban:

  • Úgy tűnt, hogy a MongoDB tárolja az állapotot. Ugyanakkor a projekt naplói megszakadtak, mert a Monga valamiért elkezdte spammelni őket, és néhányan egyszerűen kikapcsolták őket globálisan.
  • A Telegram bridzsszínésze a felismerhetetlenségig átalakult, és maga kezdte elemezni az üzeneteket.
  • A csevegés szereplőit kíméletlenül kivágták, helyettük egy színészt vettek fel, aki egyszerre rejtett el minden információt az összes csevegésről. Ez a színész minden tüsszentésnél bajba került. Nos, igen, például egy cikk információinak frissítése során nehéz elküldeni az összes csevegőszereplőnek (olyanok vagyunk, mint a Google, felhasználók milliói várnak egymillió cikket a chatben), de minden alkalommal, amikor a chat frissül, normális bemenni Mongába. Ahogy jóval később rájöttem, a chatek működési logikája is teljesen kiszakadt, és a helyén megjelent valami, ami nem működött.
  • A típusosztályoknak nyoma sem maradt.
  • Valami egészségtelen logika jelent meg a színészekben az egymásra való feliratkozásukkal, ami faji állapothoz vezetett.
  • Adatstruktúrák típusú mezőkkel Option[Int] Int-re változott olyan mágikus alapértelmezett értékekkel, mint a -1. Később rájöttem, hogy a mongoDB tárolja a json-t, és nincs semmi baj, ha ott tárolom Option nos, vagy legalább a -1-et None-ként elemezni, de akkor ezt nem tudtam, és szót fogadtam, hogy „így kell”. Ezt a kódot nem én írtam, és egyelőre nem is foglalkoztam vele.
  • Rájöttem, hogy a nyilvános IP-címem hajlamos megváltozni, és minden alkalommal hozzá kellett adnom a Mongo fehérlistájához. A botot helyben indítottam el, a Monga valahol a Monga, mint cég szerverein volt.
  • Hirtelen megszűnt a táviratok címkéinek és üzenetformázásának normalizálása. (Hmm, miért lenne az?)
  • Tetszett, hogy a bot állapotát egy külső adatbázis tárolja, és újraindításkor úgy működik tovább, mintha mi sem történt volna. Azonban ez volt az egyetlen plusz.

A második ember nem sietett különösebben, és ezek a változások már szeptember elején egy nagy kupacban jelentek meg. Nem értékeltem azonnal az ebből eredő pusztulás mértékét, és kezdtem megérteni az adatbázis munkáját, mert... Még soha nem foglalkoztam velük. Csak később jöttem rá, hogy mennyi működő kódot vágtak ki, és mennyi hibát adtak a helyére.

Szeptember

Eleinte úgy gondoltam, hogy hasznos lenne elsajátítani Mongát, és jól csinálni. Aztán lassan kezdtem megérteni, hogy az adatbázissal való kommunikáció megszervezése is olyan művészet, amiben rengeteget lehet versenyezni és csak hibázni. Például, ha a felhasználó két olyan üzenetet kap, mint /subscribe - és mindegyikre válaszul készítünk egy bejegyzést a táblázatban, mert ezen üzenetek feldolgozása idején a felhasználó nem feliratkozott. Van egy olyan gyanúm, hogy a Mongával való kommunikáció a jelenlegi formájában nem a legjobb módon van megírva. Például a felhasználó beállításai a regisztráció pillanatában lettek létrehozva. Ha megpróbálta megváltoztatni őket az előfizetés ténye előtt... a bot nem reagált semmit, mert a színészben lévő kód bement az adatbázisba a beállításokhoz, nem találta és lefagyott. Arra a kérdésre, hogy miért nem kell szükség szerint létrehozni a beállításokat, megtudtam, hogy nem kell módosítani, ha a felhasználó nem iratkozott fel... Az üzenetszűrő rendszer valahogy nem nyilvánvalóan készült, és a kód alapos áttekintése után is tudtam nem értem, hogy eredetileg így tervezték-e, vagy ott van a hiba.

A chatre beküldött cikkek listája nem volt, ehelyett azt javasolták, hogy én írjam meg őket. Ez meglepett - általában nem voltam az ellen, hogy mindenféle dolgot belerángassanak a projektbe, de logikus lenne annak, aki ezeket behozta és felcsavarta. De nem, úgy tűnt, hogy a második résztvevő mindenről lemondott, de azt mondta, hogy a chaten belüli lista állítólag rossz megoldás, és olyan eseményekkel kell jelet készíteni, mint „egy cikket küldtek x felhasználónak”. Ezután, ha a felhasználó új cikkek küldését kérte, akkor egy kérést kellett küldeni az adatbázisba, amely kiválasztja a felhasználóhoz kapcsolódó eseményeket az események közül, megkapja az új cikkek listáját, kiszűri, elküldi a felhasználónak. és dobja vissza az ezzel kapcsolatos eseményeket az adatbázisba.

A második résztvevőt valahol az absztrakciók felé vitték, amikor a bot nem csak cikkeket kap Habrtól, és nem csak táviratba küldi.

Szeptember második felére valahogy külön tábla formájában megvalósítottam az eseményeket. Nem optimális, de legalább a bot elkezdett dolgozni, és újra elkezdett cikkeket küldeni, és lassan rájöttem, mi történik a kódban.

Most visszatérhet az elejére, és ne feledje, hogy a tárolót eredetileg nem én hoztam létre. Mi történhetett így? A lehívási kérelmemet elutasították. Kiderült, hogy van redneck kódom, nem tudtam, hogyan kell csapatban dolgozni, és a jelenlegi megvalósítási görbén ki kellett javítanom a hibákat, nem pedig használható állapotba finomítani.

Felháborodtam, és megnéztem a commit előzményeket és a felírt kód mennyiségét. Megnéztem az eredetileg jól megírt, majd visszatört pillanatokat...

F*rja meg

Eszembe jutott a cikk Ön nem a Google.

Úgy gondoltam, hogy megvalósítás nélkül senkinek sincs szüksége ötletre. Arra gondoltam, hogy szeretnék egy működő botot, amely egyetlen példányban működik egyetlen számítógépen, mint egyszerű java program. Tudom, hogy a botom hónapokig fog működni újraindítás nélkül, hiszen már írtam ilyeneket a múltban. Ha hirtelen leesik, és nem küld a felhasználónak újabb cikket, akkor nem esik le az ég a földre, és nem történik semmi katasztrofális.

Miért van szükségem Dockerre, mongoDB-re és más „komoly” szoftverek cargo-kultuszára, ha a kód egyszerűen nem vagy ferdén működik?

Elágaztam a projektet, és mindent úgy csináltam, ahogy akartam.

Telegram bot a Habr. cikkeinek személyre szabott válogatásához

Körülbelül ugyanekkor munkahelyet váltottam, és nagyon hiányzott a szabadidőm. Reggel rögtön a vonaton ébredtem, este későn tértem vissza, és már nem akartam semmit sem csinálni. Egy darabig nem csináltam semmit, aztán eluralkodott rajtam a vágy, hogy befejezzem a botot, és reggel munkába menet közben elkezdtem lassan átírni a kódot. Nem mondom, hogy produktív volt: nem túl kényelmes ülni egy remegő vonaton, laptoppal az ölében, és a verem túlcsordulását nézni a telefonról. A kódírással töltött idő azonban teljesen észrevétlenül elrepült, és a projekt lassan a működő állapot felé kezdett elmozdulni.

Valahol a tudatom mélyén ott volt a kétely féreg, amely a mongoDB-t akarta használni, de úgy gondoltam, hogy a „megbízható” állapottárolás előnyei mellett vannak észrevehető hátrányai is:

  • Az adatbázis újabb kudarcponttá válik.
  • A kód egyre összetettebb, és tovább tart, amíg megírom.
  • A kód lassúvá és hatástalanná válik; ahelyett, hogy megváltoztatna egy objektumot a memóriában, a változások elküldésre kerülnek az adatbázisba, és szükség esetén visszahúzódnak.
  • Az események külön táblázatban való tárolásának típusára vonatkozóan korlátozások vonatkoznak, amelyek az adatbázis sajátosságaihoz kapcsolódnak.
  • A Monga próbaverziójának vannak bizonyos korlátai, és ha ezekbe ütközik, akkor valamin el kell indítania és be kell állítania a Mongát.

Kivágtam a mongát, most a bot állapota egyszerűen eltárolódik a program memóriájában, és időről időre elmenti egy fájlba json formátumban. Talán a kommentekbe írják, hogy tévedek, hogy itt kell használni az adatbázist stb. De ez az én projektem, a fájl megközelítése a lehető legegyszerűbb, és átlátható módon működik.

Olyan mágikus értékeket dobott ki, mint a -1, és visszaadta a normál értékeket Option, hozzáadott egy hash tábla tárolását a csevegési információkat tartalmazó objektumhoz visszaküldött cikkekkel. Az öt napnál régebbi cikkekkel kapcsolatos információk törlése hozzáadva, hogy ne tároljon mindent. A naplózást működő állapotba hoztam - a naplók ésszerű mennyiségben íródnak a fájlba és a konzolba is. Számos adminisztrátori parancs hozzáadva, például az állapot mentése vagy statisztikák lekérése, például a felhasználók és cikkek száma.

Egy csomó apróságot javítottunk: például a cikkeknél a megtekintések, kedvelések, nemtetszések és megjegyzések száma megjelenik a felhasználói szűrő áthaladásakor. Általában meglepő, hogy mennyi apróságot kellett korrigálni. Listát vezettem, ott minden „szabálytalanságot” feljegyeztem, és lehetőség szerint javítottam.

Például hozzáadtam azt a lehetőséget, hogy az összes beállítást közvetlenül egy üzenetben állítsam be:

/subscribe
/rating +20
/author a -30
/author s -20
/author p +9000
/tag scala 20
/tag akka 50

És egy másik csapat /settings pontosan ebben a formában jeleníti meg őket, akkor kiveheti belőle a szöveget, és elküldheti az összes beállítást egy barátjának.
Apróságnak tűnik, de több tucat hasonló árnyalat létezik.

Megvalósított cikkszűrés egyszerű lineáris modell formájában - a felhasználó további minősítést állíthat be a szerzők és címkék számára, valamint egy küszöbértéket. Ha a szerző értékelésének, a címkék átlagos értékelésének és a cikk tényleges értékelésének összege meghaladja a küszöbértéket, akkor a cikk megjelenik a felhasználó számára. A bottól kérhet cikkeket a /new paranccsal, vagy feliratkozhat a botra, és a nap bármely szakában személyes üzenetben küldi el a cikkeket.

Általánosságban elmondható, hogy minden cikkhez az volt az ötletem, hogy vegyek ki több funkciót (hubok, megjegyzések száma, könyvjelzők, az értékelés változásainak dinamikája, a szöveg mennyisége, képek és kódok a cikkben, kulcsszavak), és mutassak a felhasználónak egy OK/ nem ok szavazni minden egyes cikk alatt, és kiképezni egy modellt minden felhasználónak, de lusta voltam.

Ráadásul a munka logikája sem lesz annyira nyilvánvaló. Most már manuálisan beállíthatom a +9000-es besorolást a páciensZero számára, és a +20-as küszöbértékkel garantáltan megkapom az összes cikkét (kivéve persze, ha egyes címkéknél -100500-at állítok be).

A végső architektúra meglehetősen egyszerűnek bizonyult:

  1. Egy színész, aki tárolja az összes csevegés és cikk állapotát. Az állapotát a lemezen lévő fájlból tölti be, és időről időre visszamenti, minden alkalommal egy új fájlba.
  2. Egy színész, aki időnként felkeresi az RSS-hírcsatornát, új cikkeket ismer meg, megnézi a linkeket, elemzi, és elküldi ezeket a cikkeket az első szereplőnek. Emellett időnként az első szereplőtől kér cikklistát, kiválogatja a három napnál nem régebbieket, de már rég nem frissült, és frissíti.
  3. Egy színész, aki távirattal kommunikál. Még mindig teljesen idehoztam az üzenet elemzését. Békés módon két részre szeretném osztani – úgy, hogy az egyik elemzi a bejövő üzeneteket, a másik pedig a szállítási problémákkal foglalkozik, mint például az el nem küldött üzenetek újraküldése. Most nincs újraküldés, a hiba miatt meg nem érkezett üzenet pedig egyszerűen elveszik (hacsak nincs bejegyezve a naplókban), de ez eddig nem okozott gondot. Talán problémák merülnek fel, ha egy csomó ember feliratkozik a botra, és elérem az üzenetküldés határát).

Ami tetszett, hogy az akka-nak köszönhetően a 2. és 3. szereplő esései általában nem befolyásolják a bot teljesítményét. Lehet, hogy egyes cikkek nem frissülnek időben, vagy egyes üzenetek nem jutnak el a távirathoz, de a fiók újraindítja a színészt, és minden tovább működik. Azt az információt mentem el, hogy a cikk csak akkor jelenik meg a felhasználó számára, ha a távirat szereplője azt válaszolja, hogy sikeresen kézbesítette az üzenetet. A legrosszabb, ami fenyeget, hogy többször elküldöm az üzenetet (ha kézbesítik, de valahogy elveszik a visszaigazolás). Elvileg, ha az első szereplő nem tárolná magában az állapotot, hanem valamilyen adatbázissal kommunikálna, akkor ő is észrevétlenül bukhatna és visszatérhetne az életbe. Kipróbálhatnám az akka kitartást is a szereplők állapotának visszaállítására, de a mostani megvalósítás az egyszerűségével megfelel nekem. Nem arról van szó, hogy a kódom gyakran összeomlott – éppen ellenkezőleg, elég sok erőfeszítést tettem annak érdekében, hogy lehetetlenné tegyem. De megtörténik a szar, és az a képesség, hogy a programot elszigetelt darabokra-színészekre bontsuk, nagyon kényelmesnek és praktikusnak tűnt számomra.

A circle-ci-t adtam hozzá, hogy ha eltörik a kód, azonnal értesülj róla. Ez legalább azt jelenti, hogy a kód fordítása leállt. Kezdetben szerettem volna hozzáadni a travist, de csak a projektjeimet mutatta meg villák nélkül. Általában mindkét dolog szabadon használható nyílt tárolókban.

Eredményei

Már november van. A bot meg van írva, az elmúlt két hétben használtam és tetszett. Ha van fejlesztési ötleted, írj. Nem látom értelmét a bevételszerzésnek – hadd működjön, és küldjön érdekes cikkeket.

Bot link: https://t.me/HabraFilterBot
Github: https://github.com/Kright/habrahabr_reader

Kis következtetések:

  • Még egy kis projekt is sok időt vehet igénybe.
  • Ön nem a Google. Nincs értelme ágyúból verebeket lőni. Egy egyszerű megoldás is működhet.
  • A kisállat-projektek nagyon alkalmasak új technológiákkal való kísérletezésre.
  • A távirat-botokat meglehetősen egyszerűen írják. Ha nem lett volna „csapatmunka” és a technológiai kísérletek, a bot egy-két héten belül megíródott volna.
  • A színészmodell egy érdekes dolog, ami jól passzol a többszálú és hibatűrő kódhoz.
  • Azt hiszem, belekóstolhattam abba, hogy a nyílt forráskódú közösség miért szereti a villákat.
  • Az adatbázisok azért jók, mert az alkalmazás állapota már nem függ az alkalmazás összeomlásától/újraindításától, hanem az adatbázissal való munka bonyolítja a kódot és korlátozásokat szab az adatszerkezetre.

Forrás: will.com

Hozzászólás