Jak zrobić twitterowego bota?

Automaty rządzą naszym życiem. Czy tego chcemy czy nie. Szczególnie w sieciach społecznościowych, nawet podobno wygrały wybory prezydenckie w USA (ciekawe czy w Rosji też). Spróbujmy zatem samodzielnie napisać jakiś automat działający w social mediach. W R, chociaż można w czymkolwiek innym.

Przygotujemy automat (bota), który będzie na Twitterze publikował wybrane przez nas treści. Taki bot już istnieje - to @rstatspl. W tym wpisie znajdziecie informacje jak powstał. Bot @rstatspl robi trzy rzeczy:

  • Repostuje twitty z tagu #rstats - te, które są po polsku oraz te napisane przez konta z ustawionym językiem polskim. To dwie różne sprawy, bo jak się okazało w trakcie prac Twitter sam rozpoznaje (mniej lub bardziej skutecznie) język w jakim napisany jest dany twitt.
  • Repostuje posty z fanpage Dane i analizy - ot, taki forward manager :)
  • Wita nowych obserwujących i odwdzięcza się; takie “follow za follow”

Aby cokolwiek zrobić automatycznie na Twitterze potrzebujemy aplikacji korzystającej z API. Jak przygotować aplikację? Szczegółowe informacje znajdziecie na stronach Twittera dla developerów. Ja zrobiłem tak:

  • utworzyłem nowe konto @rstatspl
  • będąc zalogowanym na tym koncie utworzyłem aplikację korzystając z dedykowanej ku temu strony
  • po utworzeniu aplikacji potrzebujemy dwóch numerków: Consumer Key (API Key) oraz Consumer Secret (API Secret)

Do języka R mamy oczywiście gotową bibliotekę - rtweet, z której wykorzystamy kilka funkcji:

  • do pobierania twittów - search_tweets()
  • do wysyłania twittów - post_tweet()
  • do pobierania informacji o użytkownikach - lookup_users()

W pierwszej kolejności jednak potrzebujemy tokena, którym nasz skrypt przedstawi się API Twittera. Proces szczegółowo opisany jest na stronie biblioteki, a pokrótce chodzi o:

library(tidyverse)
library(rtweet)

# dane z kreatora aplikacji
appname <- "xxx" # nazwa naszej aplikacji
key <- "aaa" # Consumer Key (API Key)
secret <- "bbb" # Consumer Secret (API Secret)

# tworzymy token w zmiennej "twitter_token"
twitter_token <- create_token(
    app = appname,
    consumer_key = key,
    consumer_secret = secret)

# zapisujemy token na później, żeby nie tworzyć go za każdym razem
save(twitter_token, file = "twitter_token.rdata")

Powyższy kod wystarczy uruchomić tylko raz, bo później tak przygotowany token wystarczy przypisać do zmiennej środowiskowej, z której korzysta biblioteka:

Sys.setenv(TWITTER_PAT="/home/lemur/RProjects/rstartpl/twitter_token.rdata") # oczywiście podajemy własną ścieżkę

Teraz możemy już korzystać z biblioteki i naszej twitterowej aplikacji. Na przykład pobrać 100 najnowszych twittów z tagu #rstats, bez re-twittów:

rstat_tweets <- search_tweets("#rstats", type = "recent", n = 1000, include_rts = FALSE, retryonratelimit = TRUE)

Jakie dane otrzymujemy?

glimpse(rstat_tweets)
## Observations: 955
## Variables: 68
## $ status_id               <chr> "976377457052876800", "976375990866739...
## $ created_at              <dttm> 2018-03-21 08:38:20, 2018-03-21 08:32...
## $ user_id                 <chr> "220099608", "25872456", "750892662224...
## $ screen_name             <chr> "RMHoge", "drewvid", "oaggimenez", "ma...
## $ text                    <chr> "Wonders of the #rstats community: I h...
## $ source                  <chr> "Twitter Lite", "Paper.li", "Twitter W...
## $ display_text_width      <dbl> 241, 90, 221, 84, 153, 85, 136, 134, 2...
## $ reply_to_status_id      <chr> NA, NA, NA, NA, "976368001468448768", ...
## $ reply_to_user_id        <chr> NA, NA, NA, NA, "888164329677365250", ...
## $ reply_to_screen_name    <chr> NA, NA, NA, NA, "martinusrihhi", NA, "...
## $ is_quote                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FAL...
## $ is_retweet              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FAL...
## $ favorite_count          <int> 0, 0, 0, 1, 1, 0, 1, 0, 1, 3, 0, 1, 1,...
## $ retweet_count           <int> 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,...
## $ hashtags                <list> ["rstats", <"internationaldayofhappin...
## $ symbols                 <list> [NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
## $ urls_url                <list> ["stackoverflow.com/questions/2351…",...
## $ urls_t.co               <list> ["https://t.co/IdrgfZm44j", "https://...
## $ urls_expanded_url       <list> ["https://stackoverflow.com/questions...
## $ media_url               <list> [NA, NA, "http://pbs.twimg.com/media/...
## $ media_t.co              <list> [NA, NA, "https://t.co/7ADdwkyyAu", N...
## $ media_expanded_url      <list> [NA, NA, "https://twitter.com/oaggime...
## $ media_type              <list> [NA, NA, "photo", NA, NA, NA, NA, NA,...
## $ ext_media_url           <list> [NA, NA, "http://pbs.twimg.com/media/...
## $ ext_media_t.co          <list> [NA, NA, "https://t.co/7ADdwkyyAu", N...
## $ ext_media_expanded_url  <list> [NA, NA, "https://twitter.com/oaggime...
## $ ext_media_type          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ mentions_user_id        <list> [NA, NA, "854348082342973440", NA, <"...
## $ mentions_screen_name    <list> [NA, NA, "CNRSenLR", NA, <"martinusri...
## $ lang                    <chr> "en", "en", "en", "en", "en", "en", "e...
## $ quoted_status_id        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_text             <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_created_at       <dttm> NA, NA, NA, NA, NA, NA, NA, NA, NA, N...
## $ quoted_source           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_favorite_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_retweet_count    <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_user_id          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_screen_name      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_name             <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_followers_count  <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_friends_count    <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_statuses_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_location         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_description      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ quoted_verified         <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_status_id       <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_text            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_created_at      <dttm> NA, NA, NA, NA, NA, NA, NA, NA, NA, N...
## $ retweet_source          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_favorite_count  <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_user_id         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_screen_name     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_name            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_followers_count <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_friends_count   <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_statuses_count  <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_location        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_description     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ retweet_verified        <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ place_url               <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ place_name              <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ place_full_name         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ place_type              <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ country                 <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ country_code            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
## $ geo_coords              <list> [<NA, NA>, <NA, NA>, <NA, NA>, <NA, N...
## $ coords_coords           <list> [<NA, NA>, <NA, NA>, <NA, NA>, <NA, N...
## $ bbox_coords             <list> [<NA, NA, NA, NA, NA, NA, NA, NA>, <N...

Mamy bardzo dużo ciekawostek, z których coś można wybierać: ID twittu, nazwa i ID użytkownika, który twitt opublikował, czas publikacji, sama treść, wszelakie wymienione linki czy tagi, położenie geograficzne z którego opublikowany został twitt (bardzo rzadko wypełnione, nie nakręcajcie się) i wiele innych. W przypadku naszego bota interesuje nas język opublikowanego twitta - wybierzmy te, które są po polsku (według Twittera). Do opublikowania retwitta potrzebujemy ID oryginalnego twitta oraz nazwy użytkownika, który twitta opublikował. Zatem:

rstat_tweets %>%
   filter(lang == "pl") %>%
   select(status_id, screen_name, text)
status_idscreen_nametext
976322838998241281dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/4zstibrNJW
976232262076334080dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/MRV29j4UcC
976141643983327233dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/vTWu76Z531
976051031078416384dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/fU8ofzWZce
975960423282376704dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/aib1UhETOW
975869832280371200dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/MSZRWCxGgS
975779242993770496dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/CNRKKJ0u54
975688644307628032dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/xDd2qvibvx
975598041670586368dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/jvUokwc3DL
975507448890101760dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/iTJ93yfH03
975416866037534720dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/XLmhPFdjxj
975326266608975872dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/qoqlnEKLhw
975235666161340416dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/ogOv94ol6O
975145081043812352dziennikarzNajbardziej angażujący tweet o #rstats w ciągu ostatnich 6h. https://t.co/GiBxJiZAxA

Mamy listę, którą możemy przepisać na nasze konto. Bot będzie taką listę pobierał co jakiś czas, więc interesują nas tylko te twitty, których jeszcze nie przesłaliśmy dalej. Musimy sobie więc zapamiętywać co już poszło i tego po prostu nie przekazywać - będziemy to zapamiętywać w pliczku, który sobie teraz wczytamy:

# weź ostatni last_rstats_tweet_id
last_rstats_tweet_id <- readRDS("saved_data/last_rstats_tweet_id.RDS")

Początkowo zmienna last_rstats_tweet_id może być pusta (możemy jej przypisać zero), po pierwszym retwicie się ustawi odpowiednio. Bot ma przekazywać twitty po polsku oraz od polskich kont. Potrzebujemy więc wybrać polskie konta, które coś opublikowały (z listy pobranych twittów):

# lista unikalnych userów, którzy twitnęli
tweet_users <- rstat_tweets %>% distinct(screen_name) %>% pull(screen_name)

# pobieramy info o tych userach
tweet_users <- lookup_users(tweet_users)

# bierzemy tylko polskich
tweet_users_pl <- tweet_users %>% filter(account_lang == "pl") %>% pull(screen_name)

Teraz z puli pobranych twittów wybieramy te, które spełniają nasze ograniczenia:

tweets_to_rt <- rstat_tweets %>%
   # bez swoich własnych twittów oczywiscie!
   filter(screen_name != "rstatspl") %>%
   # te które po polsku lub są od polskich userow
   filter(lang == "pl" | screen_name %in% tweet_users_pl) %>%
   select(status_id, screen_name, lang) %>%
   # wybierz tylko te o ID którego nie było
   filter(!status_id %in% last_rstats_tweet_id) %>%
   # zbuduj URLa
   mutate(url = paste0(glue("https://twitter.com/{screen_name}/status/{status_id}"))) %>%
   arrange(status_id)

Dostajemy kilka twittów:

status_idscreen_namelangurl
975145081043812352dziennikarzplhttps://twitter.com/dziennikarz/status/975145081043812352
975235666161340416dziennikarzplhttps://twitter.com/dziennikarz/status/975235666161340416
975326266608975872dziennikarzplhttps://twitter.com/dziennikarz/status/975326266608975872
975416866037534720dziennikarzplhttps://twitter.com/dziennikarz/status/975416866037534720
975507448890101760dziennikarzplhttps://twitter.com/dziennikarz/status/975507448890101760
975598041670586368dziennikarzplhttps://twitter.com/dziennikarz/status/975598041670586368
975688644307628032dziennikarzplhttps://twitter.com/dziennikarz/status/975688644307628032
975770064430526464whyRconfdehttps://twitter.com/whyRconf/status/975770064430526464
975779242993770496dziennikarzplhttps://twitter.com/dziennikarz/status/975779242993770496
975842124892721154olga_mieenhttps://twitter.com/olga_mie/status/975842124892721154
975848771308478465whyRconfenhttps://twitter.com/whyRconf/status/975848771308478465
975869832280371200dziennikarzplhttps://twitter.com/dziennikarz/status/975869832280371200
975960423282376704dziennikarzplhttps://twitter.com/dziennikarz/status/975960423282376704
976051031078416384dziennikarzplhttps://twitter.com/dziennikarz/status/976051031078416384
976070741115498497olga_mieenhttps://twitter.com/olga_mie/status/976070741115498497
976141643983327233dziennikarzplhttps://twitter.com/dziennikarz/status/976141643983327233
976213656575922177kosinski_rblogenhttps://twitter.com/kosinski_rblog/status/976213656575922177
976232262076334080dziennikarzplhttps://twitter.com/dziennikarz/status/976232262076334080
976322838998241281dziennikarzplhttps://twitter.com/dziennikarz/status/976322838998241281

które w pętli możemy opublikować u siebie:

# czy jest coś nowego?
if(nrow(tweets_to_rt) != 0) {

   # tweetnij wszystkie po kolei!
   for(i in 1:nrow(tweets_to_rt)) {

      # url do twitta
      twit_url <- as.character(tweets_to_rt[i, "url"])

      # zapamiętujemy, że to najnowszy
      last_rstats_tweet_id <- c(last_rstats_tweet_id, as.character(tweets_to_rt[i, "status_id"]))

      # "nasz" twitt:
      post_tweet(status = twit_url)
   }
}

# zapisz last_rstats_tweet_id do bazy opublikowanych twittów
saveRDS(last_rstats_tweet_id, file = "saved_data/last_rstats_tweet_id.RDS")

Twitty jakie opublikujemy to kolejno:

## [1] "https://twitter.com/dziennikarz/status/975145081043812352"
## [1] "https://twitter.com/dziennikarz/status/975235666161340416"
## [1] "https://twitter.com/dziennikarz/status/975326266608975872"
## [1] "https://twitter.com/dziennikarz/status/975416866037534720"
## [1] "https://twitter.com/dziennikarz/status/975507448890101760"
## [1] "https://twitter.com/dziennikarz/status/975598041670586368"
## [1] "https://twitter.com/dziennikarz/status/975688644307628032"
## [1] "https://twitter.com/whyRconf/status/975770064430526464"
## [1] "https://twitter.com/dziennikarz/status/975779242993770496"
## [1] "https://twitter.com/olga_mie/status/975842124892721154"
## [1] "https://twitter.com/whyRconf/status/975848771308478465"
## [1] "https://twitter.com/dziennikarz/status/975869832280371200"
## [1] "https://twitter.com/dziennikarz/status/975960423282376704"
## [1] "https://twitter.com/dziennikarz/status/976051031078416384"
## [1] "https://twitter.com/olga_mie/status/976070741115498497"
## [1] "https://twitter.com/dziennikarz/status/976141643983327233"
## [1] "https://twitter.com/kosinski_rblog/status/976213656575922177"
## [1] "https://twitter.com/dziennikarz/status/976232262076334080"
## [1] "https://twitter.com/dziennikarz/status/976322838998241281"

Pierwsze zadanie wykonane - bot pobiera najnowsze twitty z tagiem #rstats i publikuje linki do tych, które Twitter uznał za napisane po polsku lub pochodzą z kont, które mają ustawiony język polski. Drugim zadaniem jest przepublikowanie na Twitterze postów z fanpage’a Dane i analizy. Do pobrania postów z fanpage’a wykorzystamy bibliotekę Rfacebook, która pobiera dane przez API Facebooka. Podobnie jak poprzednio tworzymy aplikację facebookową, a której potrzebujemy klucza (zmienna fb_app_id niżej) oraz sekretnego kodu (zmienna fb_app_secret). Podobnie potrzebujemy utworzyć token, który zapiszemy sobie na później:

library(Rfacebook)
library(lubridate)

# numery z aplikacji FB
fb_app_id = "xxxx"
fb_app_secret = "yyyy"
 
# tworzymy token aplikacji i zapisujemy do lokalnego pliku
fb_oauth <- fbOAuth(app_id = fb_app_id, app_secret = fb_app_secret)
save(fb_oauth, file="fb_token.RData")

Powyższy kod wystarczy uruchomić tylko raz. Na początek przygotujemy funkcję, która pobierze nam najnowsze posty z fanpage’a:

# wczytujemy zapisany token
load("/home/lemur/RProjects/rstatpl/fb_oauth.rda")

# lista fanpage'y z których pobieramy posty
fanpages <- c("DaneAnalizy")

# funkcja pobierająca najnowsze posty z fanpage'a
getFPPosts <- function(page_name, nposts=50) {
   fb_page <- getPage(page = page_name, token = fb_oauth, n=nposts)

   # tylko potrzebne informacje
   fb_page <- fb_page %>%
      select(id, created_time, message, likes_count, comments_count) %>%
      mutate(created_time = ymd_hms(created_time))

   return(fb_page)
}

Teraz dla każdego fanpage’a pobierzemy po jednym najnowszym poście, sprawdzamy czy już był opublikowany (po jego ID)

# już przebublikowane posty
# na początku może być zero
repost_arch <- readRDS("saved_data/fbpost_arch.RDS")

for(i in 1:length(fanpages)) {

   # kolejny fanpage
   fbpost_pagename <- fanpages[i]

   # pobieramy najnowszy post
   fb_posts <- getFPPosts(fbpost_pagename, 1)

   # ID postu
   fbpost_id <- as.character(fb_posts[1, "id"])

   # czy ten post już był?
   if(repost_arch %>% filter(post_id == fbpost_id) %>% nrow() == 0) {
      # zapamiętujemy, ze był
      repost_arch <- bind_rows(repost_arch, tibble(pagename = fbpost_pagename, post_id = fbpost_id))

      # przygotowanie tweeta
      fbpost_text <- gsub("\n\n", "\n", as.character(fb_posts[1, "message"]), fixed = TRUE)

      # na Twitter go! :)
      post_tweet(status = glue("{fbpost_text}\n\nWięcej na FB/{fbpost_pagename}: http://fb.com/{fbpost_id}"))
   }
}

W efekcie opublikujemy:


Zainteresowani?

Więcej na FB/DaneAnalizy: http://fb.com/1775448726078953_1872200053070486

Na koniec zapisujemy listę opublikowanych postów:

# zapisujemy historie postow, ktore byly
saveRDS(repost_arch, "saved_data/fbpost_arch.RDS")

Ostatnie zadanie to follow za follow i podziękowanie za obserwowanie. Algorytm jest podobny:

  • pobieramy listę obserwujących
  • porównujemy ją z poprzednim (na przykład sprzed godziny) przebiegiem
  • jeśli jest ktoś nowy to zaczynamy go obserwować i jednocześnie piszemy, że miło go widzieć

Gotowy kod (cały skrypt):

library(tidyverse)
library(glue)
library(rtweet)

# token
Sys.setenv(TWITTER_PAT="/home/lemur/RProjects/rstatpl/twitter_token.rdata")

# pobierz listę obserwowanych
rstatspl_followers <- get_followers("rstatspl", n = 5000) # max 5000, przy bardziej popularnych botach trzeba to zmienić

# wczytaj z bazy poprzednia listę
rstatspl_followers_old <- readRDS("saved_data/rstatspl_followers_old.RDS")

# sprawdź kto jest nowy
rstatspl_followers_new <- anti_join(rstatspl_followers, rstatspl_followers_old, by = "user_id")

# zacznij ich obserwować
if(nrow(rstatspl_followers_new) != 0) {
   for(i in 1:nrow(rstatspl_followers_new)) {

      user_id <- as.character(rstatspl_followers_new[i, "user_id"])

      user_info <- lookup_users(user_id)
      user_name <- as.character(user_info$screen_name)

      # obserwuj konto
      post_follow(user_id)
      
      # twittnij, że super
        post_tweet(status = glue("@{user_name} witam i życzę miłego dnia! :)"))
   }
}

# zapisz listę obserwowanych
saveRDS(rstatspl_followers, "saved_data/rstatspl_followers_old.RDS")

Ostatnia rzecz to uruchomienie bota cyklicznie. Do tego przydaje się środowisko linuxowe i cron. Dodajemy do zadań cyklicznych trzy skrypty, chociaż można je połączyć w jedno. Ja przygotowałem trzy, które uruchamiają się w podobnych cyklach, aby nie zalewać konta w jednym momencie kilkoma twittami na raz. Crontab wygląda tak:

# retweet, co 10 minut
*/10 * * * * Rscript /home/lemur/RProjects/rstatpl/retweet_rstats.R

# re-follow, co 15 minut
*/15 * * * * Rscript /home/lemur/RProjects/rstatpl/rstatspl_refollow.R

# repost z fb - co godzinę, 3 minuty po pełnej godzinie
3 * * * * Rscript /home/lemur/RProjects/rstatpl/retweet_fb.R

Co można jeszcze zrobić?

Możemy przygotować kilka list obserwowanych użytkowników Twittera i na ich podstawie coś robić. Bardzo fajne boty prowadzi @dziennikarz korzystając na przykład z własnej listy dziennikarzy. Co sześć godzin bot pobiera najnowsze twitty osób należących do listy i re-twittuje tego twita, który miał największe zaangażowanie (liczba like’ów i retwittów). Podobnie działają Najbardziej angażujący twitt o #dataviz #ddj oraz Najbardziej angażujący twitt o #rstats (który był inspiracją do całej tej zabawy). Analogiczne narzędzia (ale za każdym razem zbierające dane przy uruchomieniu aplikacji) napisałem w Shiny:

Pierwsza z dwóch powyższych aplikacji robi prostą analizę tekstu, co może być ciekawym zajęciem dla bota. Wyobrażam sobie pobranie twittów dziennikarzy (korzystając z listy @dziennikarza), przygotowanie listy tematów (na przykład analiza LDA Latent Dirichlet allocation - zobacz mój wpis na ten temat) i napisanie coś na ten temat (@Warzecha dzisiaj pisze o…, @Gzyms@Gmyz o…, a @Lis ciągle o tym samym, czyli o…). Inny pomysł to odpowiadanie na cudze twitty. Tutaj robi się już trudniej, bo trzeba przeanalizować o czym był twitt. Ewentualnie można to olać i zawsze wkurzać tekstami typu Smoleńsk, kurwa! czy też czymś w tym stylu (na przykład losowo) - to już bardziej dogadywanie… Łatwo w takim przypadku być zbanowanym (albo chociaż wyciszonym). Tak czy inaczej to w tym wariancie jest największa zabawa i największe pole do popisu. Dlatego też takim powodzeniem cieszy się maszynowe przetwarzanie tekstu w ogłoszeniach rekrutacyjnych dla data scientists - wszyscy chcą pisać boty przyjmujące zamówienia z Facebooka (z Messengera konkretnie). A teraz popatrzmy na to wszystko nieco inaczej, przy okazji afery z Facebookiem i Cambridge Analytica. Gdyby zamiast jednego bota przygotować ich wiele - kilkadziesiąt, kilkaset? Bot w sumie może być jeden, kont musi być dużo. Farma taka. Gdyby analizować treści pod określonym kontem i na przykład w odpowiedzi na twitty wychwalające rząd (jakikolwiek) wysyłać setki twittów z argumentami przeciw, pokazywaniem jakichś wpadek (vide Komorowski wizytujący Japonię czym grał PiS przed wyborami prezydenckimi), nawołujące do buntu itd itp. Jedna strona mogłaby zalać Twittera swoimi poglądami. Tak się rozprzestrzenia fake news na masową skalę. I teraz od użytkowników zależy w co uwierzą. Bądź czujny! Dlatego warto być krytycznym, warto myśleć samodzielnie, oglądać obie strony medalu (czytaj: obie stacje telewizyjne). Dlatego nie warto przyjmować niczego za pewnik - to, że jedni głośno mówią, że rozliczają Żydów, komunistów i złodziejską kastę nie znaczy że jednocześnie nie robią podobnie przyznając sobie nagrody, zmieniając prawo i podporządkowując to czy owo siłom politycznym. Za Kazikiem Staszewskim: Żelazne prawo oligarchii: kto władzę ma najpierw o nią się martwi. Bądźcie czujni!