Telegrami robot Habri artiklite isikupärastamiseks

Küsimustele nagu "miks?" seal on vanem artikkel - Natural Geektimes – muudab ruumi puhtamaks.

Artikleid on palju, subjektiivsetel põhjustel mõned neist mulle ei meeldi ja mõned, vastupidi, on kahju vahele jätta. Sooviksin seda protsessi optimeerida ja aega säästa.

Ülaltoodud artikkel soovitas brauserisisene skriptimise lähenemisviisi, kuid mulle see ei meeldinud (kuigi olen seda varem kasutanud) järgmistel põhjustel:

  • Arvuti/telefoni erinevate brauserite jaoks peate selle võimaluse korral uuesti konfigureerima.
  • Autorite range filtreerimine ei ole alati mugav.
  • Probleem autoritega, kelle artikleid ei taha ilma jääda, isegi kui need ilmuvad kord aastas, pole lahendatud.

Artiklite hinnangute põhjal saidile sisseehitatud filtreerimine ei ole alati mugav, kuna kõrgelt spetsialiseerunud artiklid võivad oma väärtusest hoolimata saada üsna tagasihoidliku hinnangu.

Algselt tahtsin genereerida RSS-voogu (või isegi mitu), jättes sinna ainult huvitavad asjad. Aga lõpuks selgus, et RSS-i lugemine ei tundunud kuigi mugav: igal juhul tuleb artikli kommenteerimiseks/hääletamiseks/lemmikute hulka lisamiseks brauser läbi käia. Seetõttu kirjutasin telegrammiroti, mis saadab mulle isikliku sõnumiga huvitavaid artikleid. Telegram ise teeb neist kauneid eelvaateid, mis koos infoga autori/hinnangu/vaadete kohta näevad üsna informatiivsed välja.

Telegrami robot Habri artiklite isikupärastamiseks

Lõike all on detailid nagu töö iseärasused, kirjutamisprotsess ja tehnilised lahendused.

Lühidalt robotist

Hoidla: https://github.com/Kright/habrahabr_reader

Bot telegrammis: https://t.me/HabraFilterBot

Kasutaja määrab siltidele ja autoritele täiendava hinnangu. Pärast seda rakendatakse artiklitele filter – liidetakse artikli hinnang Habré kohta, autori kasutajahinnang ja kasutajate hinnangute keskmine sildi järgi. Kui summa on suurem kui kasutaja määratud lävi, läbib artikkel filtri.

Boti kirjutamise kõrvaleesmärk oli saada lõbu ja kogemusi. Lisaks tuletasin seda endale regulaarselt meelde Ma ei ole Google, ja seetõttu tehakse paljusid asju võimalikult lihtsalt ja isegi primitiivselt. See aga ei takistanud roboti kirjutamise protsessil kolm kuud aega võtta.

Väljas oli suvi

Juuli oli lõppemas ja ma otsustasin kirjutada roboti. Ja mitte üksi, vaid koos sõbraga, kes meisterdas scalat ja tahtis sellele midagi kirjutada. Algus tundus paljulubav - koodi lõikab tiim, ülesanne tundus lihtne ja arvasin, et paari nädala või kuu pärast on bot valmis.

Vaatamata sellele, et olen ise viimastel aastatel aeg-ajalt kaljule koodi kirjutanud, ei näe ega vaata seda koodi tavaliselt keegi: lemmikloomaprojektid, mõne idee testimine, andmete eeltöötlus, FP-st mõne kontseptsiooni masterdamine. Mind huvitas väga, kuidas näeb välja koodi kirjutamine meeskonnas, sest koodi kivile saab kirjutada väga erineval viisil.

Mis oleks võinud minna nii? Ärgem siiski asjadega kiirustagem.
Kõike, mis juhtub, saab jälgida täitmisajaloo abil.

Üks tuttav tegi repositooriumi 27. juulil, aga midagi muud ei teinud, hakkasin koodi kirjutama.

30 juuli

Lühidalt: kirjutasin Habri rss-kanali parsimise.

  • com.github.pureconfig Typeafe'i konfiguratsioonide lugemiseks otse käändeklassidesse (see osutus väga mugavaks)
  • scala-xml xml-i lugemiseks: kuna algselt tahtsin rss-kanali jaoks oma teostust kirjutada ja rss-voog on xml-vormingus, siis kasutasin parsimiseks seda teeki. Tegelikult ilmus ka RSS-i sõelumine.
  • scalatest testide jaoks. Isegi tillukeste projektide puhul säästab testide kirjutamine aega – näiteks xml parsimise silumisel on palju lihtsam seda faili alla laadida, teste kirjutada ja vigu parandada. Kui hiljem ilmnes viga mõne kummalise, vigaste utf-8 tähemärkidega html-i parsimisel, osutus mugavamaks see faili panna ja test lisada.
  • näitlejad Akkast. Objektiivselt polnud neid üldse vaja, aga projekt sai nalja pärast kirjutatud, tahtsin proovida. Selle tulemusena olen valmis ütlema, et see meeldis mulle. OOP ideed võib vaadata ka teisest küljest – on näitlejaid, kes sõnumeid vahetavad. Veelgi huvitavam on see, et saate (ja peaksite) koodi kirjutama nii, et sõnum ei pruugi kohale jõuda või seda ei töödelda (üldiselt öeldes ei tohiks sõnumeid kaduda, kui konto töötab ühes arvutis). Alguses kratsisin kukalt ja koodis oli prügi, kus näitlejad üksteist tellisid, kuid lõpuks õnnestus mul välja mõelda üsna lihtne ja elegantne arhitektuur. Iga näitleja sees olevat koodi võib pidada ühelõimeliseks, kui näitleja jookseb kokku, käivitab acca selle uuesti – tulemuseks on üsna veakindel süsteem.

9 august

Lisasin projekti scala-scrapper Habri html-lehtede sõelumiseks (näiteks artikli reitingu, järjehoidjate arvu jne väljatõmbamiseks).

Ja Kassid. Need, mis on kivis.

Telegrami robot Habri artiklite isikupärastamiseks

Seejärel lugesin raamatut hajutatud andmebaaside kohta, mulle meeldis idee CRDT-st (konfliktivaba replikeeritud andmetüüp, https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type, habr), seega postitasin Habré artikli kohta teabe saamiseks kommutatiivse poolrühma tüübiklassi.

Tegelikult on idee väga lihtne – meil on loendurid, mis muutuvad monotoonselt. Kampaaniate arv kasvab järk-järgult, nagu ka plusside arv (nagu ka miinuste arv). Kui mul on artikli kohta teabest kaks versiooni, siis saan need "üheks liita" – asjakohasemaks peetakse loenduri olekut, mis on suurem.

Poolrühm tähendab, et kaks objekti, mis sisaldavad teavet artikli kohta, saab liita üheks. Kommutatiivne tähendab, et saab liita nii A + B kui ka B + A, tulemus ei sõltu järjestusest ja lõpuks jääb alles uusim versioon. Muide, siin on ka assotsiatiivsust.

Näiteks, nagu plaanitud, andis rss pärast sõelumist artikli kohta veidi nõrgenenud teavet – ilma mõõdikuteta, nagu vaatamiste arv. Seejärel võttis spetsiaalne näitleja artiklite kohta teavet ja jooksis html-lehtedele, et seda värskendada ja vana versiooniga liita.

Üldiselt, nagu akkas, polnud selleks vajadust, sai lihtsalt updateDate'i artikli jaoks salvestada ja uuema ilma liitmisteta võtta, kuid seikluste tee viis mind.

12 august

Hakkasin end vabamalt tundma ja nalja pärast tegin igast vestlusest eraldi näitleja. Teoreetiliselt kaalub näitleja ise umbes 300 baiti ja neid saab luua miljoneid, nii et see on täiesti tavaline lähenemine. Mulle tundub, et lahendus osutus päris huvitavaks:

Üks näitleja oli sillaks Akka telegrammiserveri ja sõnumisüsteemi vahel. Ta sai lihtsalt sõnumeid ja saatis need soovitud vestluspartnerile. Vestlusnäitleja võiks vastuseks midagi tagasi saata – ja see saadetakse tagasi telegrammi. Väga mugav oli see, et see näitleja osutus võimalikult lihtsaks ja sisaldas ainult sõnumitele vastamise loogikat. Muide, teavet uute artiklite kohta tuli igasse vestlusesse, kuid jällegi ma ei näe selles probleeme.

Üldiselt robot juba töötas, vastas sõnumitele, salvestas kasutajale saadetud artiklite nimekirja ja ma juba mõtlesin, et bot on peaaegu valmis. Lisasin aeglaselt väikseid funktsioone, nagu autorinimede ja siltide normaliseerimine (sd f asendamine s_d_f-ga).

Alles oli vaid üks asi väike aga — riiki ei päästetud kuskil.

Kõik läks valesti

Võib-olla olete märganud, et kirjutasin robotit enamasti üksi. Seega osales arenduses teine ​​osaleja ja koodis ilmnesid järgmised muudatused:

  • MongoDB näis salvestavat olekut. Samal ajal läksid projekti logid katki, kuna Monga hakkas neid millegipärast spämmima ja osa inimesi lülitas need lihtsalt globaalselt välja.
  • Telegrami bridžinäitleja muutus tundmatuseni ja hakkas ise sõnumeid sõeluma.
  • Vestluste näitlejad lõigati halastamatult välja ja nende asemel asendati näitleja, kes peitis korraga kogu teabe kõigi vestluste kohta. Iga aevastamise pärast läks see näitleja hätta. Noh, jah, nagu artikliteabe värskendamisel, on selle saatmine kõigile vestlusosalistele keeruline (me oleme nagu Google, miljonid kasutajad ootavad vestluses miljon artiklit igaühe kohta), kuid iga kord, kui vestlust värskendatakse, Mongasse minek on normaalne. Nagu ma palju hiljem aru sain, oli ka vestluste tööloogika täielikult välja lõigatud ja selle asemele ilmus midagi, mis ei töötanud.
  • Tüübiklassidest pole jälgegi jäänud.
  • Näitlejates on ilmnenud mingi ebatervislik loogika, kui nad on üksteisega liitunud, mis on viinud rassiseisundini.
  • Andmestruktuurid tüübiväljadega Option[Int] muudeti Int maagiliste vaikeväärtustega nagu -1. Hiljem sain aru, et mongoDB salvestab jsoni ja selle talletamisel pole midagi halba Option noh, või vähemalt sõeluda -1 kui None, aga tol ajal ma seda ei teadnud ja võtsin sõna, et "nii see peabki olema". Ma ei kirjutanud seda koodi ja ma ei viitsinud seda praegu muuta.
  • Sain teada, et mu avalik IP-aadress kipub muutuma ja iga kord pidin selle Mongo valgesse nimekirja lisama. Käivitasin roboti kohapeal, Monga oli kuskil Monga kui ettevõtte serverites.
  • Järsku kadus telegrammide siltide ja sõnumite vormindamise normaliseerimine. (Hmm, miks see nii oleks?)
  • Mulle meeldis, et roboti olek salvestatakse välisesse andmebaasi ja taaskäivitamisel töötab see edasi, nagu poleks midagi juhtunud. See oli aga ainuke pluss.

Teisel inimesel polnud erilist kiiret ning kõik need muutused ilmnesid ühes suures hunnikus juba septembri alguses. Ma ei mõistnud koheselt tekkinud hävingu ulatust ja hakkasin andmebaasi tööst aru saama, sest... Ma pole nendega kunagi varem tegelenud. Alles hiljem sain aru, kui palju töötavat koodi lõigati ja kui palju vigu selle asemele lisati.

September

Algul arvasin, et oleks kasulik Mongat meisterdada ja hästi teha. Siis hakkasin tasapisi mõistma, et ka andmebaasiga suhtluse korraldamine on kunst, mille käigus saab palju rassida ja lihtsalt vigu teha. Näiteks kui kasutaja saab kaks sõnumit nagu /subscribe - ja vastuseks igaühele loome tabelisse kirje, kuna nende sõnumite töötlemise ajal pole kasutaja tellitud. Mul on kahtlus, et Mongaga suhtlemine praegusel kujul pole just kõige paremini kirjutatud. Näiteks kasutaja seaded loodi hetkel, kui ta registreerus. Kui ta proovis neid enne tellimise fakti muuta... bot ei vastanud midagi, sest näitlejas olev kood läks seadete andmebaasi, ei leidnud seda ja jooksis kokku. Kui küsiti, miks mitte seadistusi vastavalt vajadusele luua, sain teada, et neid pole vaja muuta, kui kasutaja pole tellinud... Sõnumite filtreerimise süsteem sai tehtud kuidagi mitteilmselt ja isegi pärast koodi põhjalikku vaatamist sain ei saa aru, kas see oli algselt nii mõeldud või on seal viga.

Vestlusse saadetud artiklite nimekirja ei olnud, selle asemel soovitati need ise kirjutada. See üllatas mind - üldiselt ma ei olnud igasuguste asjade projekti sisse vedamise vastu, aga see oleks loogiline sellele, kes need asjad sisse tõi ja kinni keeras. Aga ei, tundus, et teine ​​osaleja loobus kõigest, kuid ütles, et vestluse sees olev nimekiri oli väidetavalt halb lahendus ja selleks on vaja teha silt sündmustega nagu "kasutajale x saadeti artikkel y". Seejärel, kui kasutaja soovis saata uusi artikleid, oli vaja saata päring andmebaasi, mis selekteeriks sündmuste hulgast välja kasutajaga seotud sündmused, saaks ka uute artiklite nimekirja, filtreeriks, saadaks kasutajale ja viska selleteemalised sündmused andmebaasi tagasi.

Teine osaleja kandus kuhugi abstraktsioonide poole, kui bot saab Habrilt mitte ainult artikleid, vaid saadetakse mitte ainult telegrammi.

Teostasin üritused kuidagi eraldi märgi näol septembri teiseks pooleks. See pole optimaalne, kuid vähemalt hakkas robot tööle ja hakkas mulle uuesti artikleid saatma ning sain aeglaselt aru, mis koodis toimub.

Nüüd võite minna tagasi algusesse ja meeles pidada, et hoidla pole algselt minu loodud. Mis võis niimoodi minna? Minu tõmbamistaotlus lükati tagasi. Selgus, et mul oli rednecki kood, et ma ei teadnud, kuidas meeskonnas töötada, ja pidin praeguses juurutuskõveras vead parandama, mitte viimistlema kasutatavasse olekusse.

Ma ärritusin ja vaatasin sissekande ajalugu ja kirjutatud koodi hulka. Vaatasin hetki, mis olid algselt hästi kirjutatud ja siis murdusid tagasi...

Kurat seda

Mulle meenus artikkel Sa ei ole Google.

Arvasin, et ilma teostuseta ideed pole tegelikult kellelgi vaja. Mõtlesin, et tahan omada töötavat robotit, mis töötaks ühes eksemplaris ühes arvutis lihtsa java programmina. Tean, et mu robot töötab kuid ilma taaskäivitusteta, kuna olen selliseid roboteid juba varem kirjutanud. Kui see ootamatult kukub ja kasutajale teist artiklit ei saada, siis taevas maa alla ei kuku ja midagi katastroofilist ei juhtu.

Miks on mul vaja Dockerit, mongoDB-d ja muud "tõsise" tarkvara kaubakultust, kui kood lihtsalt ei tööta või töötab viltu?

Lõpetasin projekti ja tegin kõik nii, nagu tahtsin.

Telegrami robot Habri artiklite isikupärastamiseks

Umbes samal ajal vahetasin töökohta ja vabast ajast jäi tohutult puudu. Hommikul ärkasin kohe rongis, õhtul tulin hilja tagasi ja ei tahtnud enam midagi teha. Tükk aega ei teinud ma midagi, siis sai minust võimust soov bot valmis saada ja hakkasin hommikul tööle sõites aeglaselt koodi ümber kirjutama. Ma ei ütle, et see oli produktiivne: istuda raputavas rongis, sülearvuti süles ja vaadata telefonist virna ülevoolu, pole eriti mugav. Koodi kirjutamisele kulunud aeg lendas aga täiesti märkamatult mööda ning projekt hakkas tasapisi liikuma tööoleku poole.

Kusagil kuklas oli kahtluseuss, mis tahtis mongoDB-d kasutada, kuid arvasin, et lisaks “usaldusväärse” olekusalvestuse eelistele on ka märgatavaid puudusi:

  • Andmebaasist saab veel üks tõrkepunkt.
  • Kood muutub keerulisemaks ja selle kirjutamine võtab rohkem aega.
  • Kood muutub aeglaseks ja ebaefektiivseks, mälus oleva objekti muutmise asemel saadetakse muudatused andmebaasi ja tõmmatakse vajadusel tagasi.
  • Sündmuste eraldi tabelis salvestamise tüübile on kehtestatud piirangud, mis on seotud andmebaasi iseärasustega.
  • Monga prooviversioonil on mõned piirangud ja kui nendega kokku puutute, peate Monga millegi jaoks käivitama ja konfigureerima.

Lõikasin monga välja, nüüd salvestatakse roboti olek lihtsalt programmi mällu ja salvestatakse aeg-ajalt json-vormingus faili. Võib-olla kirjutavad nad kommentaaridesse, et ma eksin, et siin tuleks andmebaasi kasutada jne. Aga see on minu projekt, lähenemine failile on võimalikult lihtne ja toimib läbipaistvalt.

Viskas välja maagilised väärtused nagu -1 ja tagastas normaalsed Option, lisati vestlusteabega objektile tagasi saadetud artiklitega räsitabeli salvestus. Lisatud on teabe kustutamine üle viie päeva vanuste artiklite kohta, et mitte kõike salvestada. Viisin logimise tööolekusse - logisid kirjutatakse mõistlikus koguses nii faili kui konsooli. Lisatud on mitu administraatori käsku, näiteks oleku salvestamine või statistika hankimine, näiteks kasutajate ja artiklite arv.

Parandasime hunniku pisiasju: näiteks artiklite puhul näidatakse nüüd vaatamiste, meeldimiste, mittemeeldimiste ja kommentaaride arv kasutaja filtri läbimise ajal. Üldiselt on üllatav, kui palju pisiasju tuli parandada. Pidasin nimekirja, märkisin üles kõik "ebakorrapärasused" ja parandasin neid nii palju kui võimalik.

Näiteks lisasin võimaluse määrata kõik seaded otse ühes sõnumis:

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

Ja veel üks meeskond /settings kuvab need täpselt sellisel kujul, saate sealt teksti võtta ja kõik seaded sõbrale saata.
Tundub väike asi, aga sarnaseid nüansse on kümneid.

Rakendatud artiklite filtreerimine lihtsa lineaarse mudeli kujul – kasutaja saab määrata autoritele ja siltidele lisareitingu ning läviväärtuse. Kui autori hinnangu, siltide keskmise hinnangu ja artikli tegeliku hinnangu summa on suurem kui läviväärtus, näidatakse artiklit kasutajale. Saate kas küsida robotilt artikleid käsuga /new või tellida boti ja see saadab artikleid isikliku sõnumiga igal kellaajal.

Üldiselt tuli mul mõte igast artiklist välja tõmmata rohkem funktsioone (jaoturid, kommentaaride arv, järjehoidjad, reitingute muutuste dünaamika, teksti hulk, pildid ja kood artiklis, märksõnad) ja näidata kasutajale ok/ ei ole ok hääletada iga artikli all ja koolitada iga kasutaja jaoks mudelit, aga ma olin liiga laisk.

Lisaks ei jää töö loogika nii ilmne. Nüüd saan patsiendi nulli jaoks käsitsi määrata hinnanguks +9000 ja lävehinnanguga +20 saan garanteeritud, et saan kätte kõik tema artiklid (kui ma muidugi ei määra mõne sildi jaoks -100500).

Lõplik arhitektuur osutus üsna lihtsaks:

  1. Näitleja, kes salvestab kõigi vestluste ja artiklite oleku. See laadib oma oleku kettal olevast failist ja salvestab selle aeg-ajalt tagasi, iga kord uude faili.
  2. Näitleja, kes külastab aeg-ajalt RSS-kanalit, õpib tundma uusi artikleid, vaatab linke, analüüsib ja saadab need artiklid esimesele näitlejale. Lisaks küsib see mõnikord esimeselt näitlejalt artiklite nimekirja, valib välja need, mis pole vanemad kui kolm päeva, kuid mida pole pikka aega uuendatud, ja uuendab neid.
  3. Näitleja, kes suhtleb telegrammiga. Tõin ikka sõnumi parsimise täielikult siia. Sõbralikult tahaksin selle jagada kaheks – nii, et üks parsib sissetulevaid sõnumeid ja teine ​​tegeleb transpordiprobleemidega, näiteks saatmata sõnumite uuesti saatmisega. Nüüd enam uuesti saatmist ei toimu ja vea tõttu saabumata sõnum läheb lihtsalt kaotsi (kui just logidesse märgitud pole), kuid siiani pole see probleeme tekitanud. Võib-olla tekivad probleemid, kui hulk inimesi tellib roboti ja ma jõuan sõnumite saatmise limiidini).

Mulle meeldis see, et tänu akkale ei mõjuta näitlejate 2 ja 3 kukkumised üldiselt boti jõudlust. Võib-olla ei värskendata mõnda artiklit õigel ajal või mõni sõnum ei jõua telegrammi, kuid konto taaskäivitab näitleja ja kõik töötab edasi. Salvestan info, et artiklit näidatakse kasutajale alles siis, kui telegramminäitleja vastab, et on sõnumi edukalt edastanud. Kõige hullem, mis mind ähvardab, on sõnumi mitu korda saatmine (kui see toimetatakse, aga kinnitus on kuidagi kadunud). Põhimõtteliselt, kui esimene näitleja ei salvestaks olekut enda sisse, vaid suhtleks mingi andmebaasiga, siis võis ka tema märkamatult kukkuda ja ellu naasta. Näitlejate seisundi taastamiseks võiks proovida ka akka püsivust, aga praegune teostus sobib mulle oma lihtsusega. Asi pole selles, et mu kood jooksis sageli kokku – vastupidi, ma nägin üsna palju vaeva, et see võimatuks muuta. Aga jama juhtub ja võimalus programm üksikuteks tükkideks-näitlejateks jaotada tundus mulle väga mugav ja praktiline.

Lisasin circle-ci, et kui kood katki läheb, siis saad sellest kohe teada. Vähemalt tähendab see, et kood on kompileerimise lõpetanud. Algselt tahtsin lisada travise, kuid see näitas ainult minu projekte ilma kahvliteta. Üldiselt saab neid mõlemaid asju vabalt kasutada avatud hoidlates.

Tulemused

Käes on juba november. Bot on kirjutatud, olen seda viimased kaks nädalat kasutanud ja mulle meeldis. Kui teil on ideid parandamiseks, kirjutage. Ma ei näe selle monetiseerimisel mõtet – laske sellel lihtsalt töötada ja saatke huvitavaid artikleid.

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

Väikesed järeldused:

  • Isegi väike projekt võib võtta kaua aega.
  • Sa ei ole Google. Varblasi kahurist tulistada pole mõtet. Lihtne lahendus võib sama hästi toimida.
  • Lemmikloomaprojektid sobivad väga hästi uute tehnoloogiatega katsetamiseks.
  • Telegrami robotid on kirjutatud üsna lihtsalt. Kui see poleks olnud meeskonnatöö ja tehnoloogiaga katsetused, oleks robot kirjutatud nädala või kahega.
  • Näitlejamudel on huvitav asi, mis sobib hästi mitme keermega ja tõrketaluva koodiga.
  • Ma arvan, et sain maitse sellest, miks avatud lähtekoodiga kogukond armastab kahvleid.
  • Andmebaasid on head, sest rakenduse olek ei sõltu enam rakenduse kokkujooksmistest/taaskäivitustest, vaid andmebaasiga töötamine muudab koodi keerulisemaks ja seab andmestruktuurile piiranguid.

Allikas: www.habr.com

Lisa kommentaar