You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

poputweets 3.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #!/usr/bin/env ruby
  2. require 'json'
  3. require 'pp'
  4. require 'time'
  5. require 'pry'
  6. ME = "67204542"
  7. class Tweet
  8. attr_reader :children
  9. attr_accessor :parent
  10. def initialize(hash)
  11. @data = {}
  12. %w(favorite_count id_str retweet_count created_at full_text
  13. in_reply_to_status_id_str in_reply_to_user_id_str
  14. in_reply_to_screen_name).each do |key|
  15. @data[key] = hash.delete(key)
  16. end
  17. if hash.length > 0
  18. raise "unhandled data remains: #{hash.inspect}"
  19. end
  20. @children = []
  21. @parent = nil
  22. end
  23. def favs
  24. @data["favorite_count"].to_i
  25. end
  26. def rts
  27. @data["retweet_count"].to_i
  28. end
  29. def id
  30. @data["id_str"]
  31. end
  32. def created_at
  33. Time.parse(@data["created_at"])
  34. end
  35. def text
  36. @data["full_text"]
  37. end
  38. def in_reply_to
  39. status_id = @data["in_reply_to_status_id_str"]
  40. user_id = @data["in_reply_to_user_id_str"]
  41. screen_name = @data["in_reply_to_screen_name"]
  42. screen_name && {
  43. status_id: status_id,
  44. user_id: user_id,
  45. screen_name: screen_name,
  46. }
  47. end
  48. def in_reply_to_invalid?
  49. @data["in_reply_to_user_id_str"] && !@data["in_reply_to_screen_name"]
  50. end
  51. def all_children
  52. all = []
  53. stack = children.dup
  54. while node = stack.pop
  55. all << node
  56. stack += node.children
  57. end
  58. all
  59. end
  60. def inspect
  61. all = all_children
  62. if all.any?
  63. chn = "(#{all.length} children) "
  64. else
  65. chn = ""
  66. end
  67. "<Tweet: #{created_at} #{chn}(#{favs}/#{rts})>"
  68. end
  69. def root
  70. if parent.nil?
  71. self
  72. else
  73. parent.root
  74. end
  75. end
  76. end
  77. data = File.read(ENV["POPUTWEET_JS"] || "tweet.js")
  78. tweets = JSON.parse(data[data.index("[")..-1])
  79. tweets.map! do |tweet|
  80. tweet = tweet["tweet"]
  81. %w(possibly_sensitive entities source display_text_range lang
  82. extended_entities retweeted favorited truncated
  83. id in_reply_to_user_id in_reply_to_status_id
  84. withheld_in_countries).each do |key|
  85. tweet.delete(key)
  86. end
  87. Tweet.new(tweet)
  88. end
  89. tweets.reject! do |tweet|
  90. tweet.text.start_with?("RT @") ||
  91. tweet.in_reply_to_invalid? ||
  92. begin
  93. in_reply_to = tweet.in_reply_to
  94. in_reply_to && in_reply_to[:user_id] != ME
  95. end
  96. end
  97. tweets_by_id = {}
  98. tweets.each do |tweet|
  99. tweets_by_id[tweet.id] = tweet
  100. end
  101. tweets.reject! do |tweet|
  102. if in_reply_to = tweet.in_reply_to
  103. status_id = in_reply_to[:status_id]
  104. if t = tweets_by_id[status_id]
  105. t.children << tweet
  106. tweet.parent = t
  107. else
  108. tweets_by_id.delete tweet.id
  109. next true
  110. end
  111. end
  112. false
  113. end
  114. roots = {}
  115. tweets.each do |tweet|
  116. root = tweet.root
  117. next if roots[root.id]
  118. roots[root.id] = root
  119. end
  120. by_score = {}
  121. roots.values.each do |root|
  122. favs = root.favs + root.all_children.map(&:favs).sum
  123. rts = root.rts + root.all_children.map(&:rts).sum
  124. score = (root.all_children.length + 1)*(favs*4 + rts*8)
  125. by_score[score] ||= []
  126. by_score[score] << root
  127. end
  128. by_score.keys.sort.reverse[0...100].each do |score|
  129. puts "Score: #{score}"
  130. by_score[score].each do |t|
  131. puts " #{t.inspect}"
  132. puts " https://twitter.com/kivikakk/status/#{t.id}"
  133. puts t.text.chars.each_slice(90).map {|l| " #{l.join}"}.join("\n")
  134. end
  135. end