1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
# -*- coding: utf-8 -*-
Plugin.create :list do
defevent :list_created, priority: :routine_passive, prototype: [Service, UserLists]
defevent :list_destroy, priority: :routine_passive, prototype: [Service, Array]
crawl_count = Hash.new{|h,k|h[k] = gen_counter}
on_period do |service|
if crawl_count[service].call >= UserConfig[:retrieve_interval_list_timeline]
crawl_count[service] = gen_counter
fetch_and_modify_for_using_lists(service)
end
end
filter_extract_datasources do |datasources|
result = available_lists.inject(datasources||{}) do |_datasources, list|
_datasources.merge datasource_slug(list) => ["@#{list.user.idname}", 'list'.freeze, list[:name]] end
[result] end
def datasource_slug(list)
type_strict list => UserList
:"#{list.user.idname}_list_#{list[:id]}" end
# available_list の同期をとる。外的要因でリストが追加されたのを検出した場合。
on_list_created do |service, lists|
created = lists.reject{ |list| available_lists.include?(list) }
set_available_lists(service, available_lists(service) + created) if not created.empty? end
# available_list の同期をとる。外的要因でリストが削除されたのを検出した場合。
on_list_destroy do |service, lists|
deleted = lists.select{ |list| available_lists.include?(list) }
set_available_lists(service, available_lists(service) - deleted) if not deleted.empty? end
# フォローしているリストを返す
filter_following_lists do |lists|
[lists | available_lists] end
# リストのタイムラインをリアルタイム更新する
on_appear do |messages|
using_lists.each do |list|
Plugin.call(:extract_receive_message, datasource_slug(list), messages.lazy.select(&list.method(:related?))) end end
on_service_registered do |service|
if service
fetch_and_modify_for_using_lists(service) end end
on_service_destroyed do |service|
service.lists(cache: true, user: service.user_obj).next{ |lists|
Plugin.call(:list_destroy, service, lists) if lists
}.terminate
end
# FILTER stream で、タイムラインを表示しているユーザをフォロー
filter_filter_stream_follow do |users|
[using_lists.inject(users){ |r, list| r.merge(list.member) }] end
# _service_ が作成またはフォローしている全てのリストを取得する。
# ただし、自分のアカウント(A)で作成したリストを、自分のアカウント(B)でフォローしている場合、
# fetch_list_of_service(B) の結果にそのリストは含まれず、
# fetch_list_of_service(A) の結果からしか見ることができない。
# ==== Args
# [service] リストのオーナーのServiceオブジェクト
# [cache] キャッシュの利用方法
# ==== Return
# deferred
def fetch_list_of_service(service, cache=:keep)
type_strict service => Service
service.lists(cache: cache, user: service.user_obj).next do |lists|
if lists
set_available_lists(service, lists)
Service.reject(&service.method(:==)).map(&:user).inject(lists.lazy) do |stream, u|
stream.reject{|l| l.user == u } end end end end
# リストのメンバーを取得する
# ==== Args
# [list] リスト
# [cache] キャッシュの利用方法
# [service] list にアクセス可能なユーザのService
# ==== Return
# deferred
def list_modify_member(list, cache: :keep, service: fail)
service.list_members( list_id: list[:id],
mode: list[:mode],
cache: false).next{ |users|
list.add_member(users) if users
service.list_statuses(:id => list[:id],
:cache => cache).next{ |res|
if res.is_a?(Array) and using?(list)
Plugin.call(:extract_receive_message, datasource_slug(list), res) end
}.terminate
}.trap { |error|
if defined?(error.httpresponse.code) && 404 == error.httpresponse.code.to_i
Plugin.call(:list_destroy, service, [list])
Plugin.activity :error, _("リスト「%{list_name} (#%{list_id})」は削除されているか、@%{screen_name} が閲覧することを禁止されています") % {
screen_name: service.user_obj.idname,
list_id: list[:id],
list_name: list[:full_name] }
else
Deferred.fail(error)
end }.terminate(_("リスト %{list_name} (#%{list_id}) のメンバーの取得に失敗しました") % {
list_name: list[:full_name],
list_id: list[:id] }) end
# 現在データソースで使用されているリストを返す
# ==== Return
# Enumerable データソースで使われているリスト
def using_lists
list_ids = Set.new Plugin.filtering(:extract_tabs_get, []).first.map{|tab|
tab[:sources]
}.select{ |sources|
sources.is_a? Enumerable
}.inject(Set.new, &:merge).map{ |source|
$1.to_i if source.to_s =~ /[a-zA-Z0-9_]+_list_(\d+)/
}.compact
available_lists.lazy.select do |list|
list_ids.include? list[:id] end end
# list が抽出タブで使われていて、更新を要求されているなら真を返す
# ==== Args
# [list] UserList 調べるリスト
# ==== Return
# 使われていたら真
def using?(list)
using_lists.include? list end
# _service_ が所有するリストを更新し、そのうち実際に抽出タブで使用されているリストについて、
# リストのメンバーの更新と、直近のツイートの取得を行う
# ==== Args
# [service] Service
def fetch_and_modify_for_using_lists(service, cache=:keep)
fetch_list_of_service(service, cache).next{|service_that_has_list|
modifier = (Set.new(service_that_has_list) & Set.new(using_lists)).map{|list| list_modify_member(list, service: service, cache: cache)}
Deferred.when(*modifier) unless modifier.empty?
}.terminate(_('%{user_name}がフォローしているリストを取得できませんでした') % {user_name: service.user_obj.idname}) end
# 自分がフォローしているリストを返す。
# _service_ を指定すると、そのアカウントでフォローしているリストに結果を限定する。
# 結果は重複する可能性がある
# ==== Args
# [service] Service|nil リストのフォロイーで絞り込む場合、そのService
# ==== Return
# Enumerable 自分がフォローしているリスト(UserList)を列挙する
def available_lists(service = nil)
@available_lists ||= Hash.new
if service
@available_lists[service.user_obj] ||= UserLists.new.freeze
else
@all_available_lists ||= UserLists.new(@available_lists.flat_map{|k,v| v}.uniq.compact).freeze end end
# _service_ がフォローしているリストを新しく設定する
# ==== Args
# [service] Service リストのフォロイー TODO: これ実装する
# [newlist] Enumerable 新しいリスト
# ==== Return
# self
def set_available_lists(service, newlist)
type_strict service => Service, newlist => Enumerable
newlist_ary = newlist.to_a
available_list_of_service = available_lists(service).to_a
created = newlist_ary - available_list_of_service
deleted = available_list_of_service - newlist_ary
Plugin.call(:list_created, service, UserLists.new(created)) if not created.empty?
Plugin.call(:list_destroy, service, UserLists.new(deleted)) if not deleted.empty?
@available_lists[service.user_obj] = UserLists.new(newlist_ary).freeze
@all_available_lists = nil
Plugin.call(:list_data, service, available_lists(service)) if not(created.empty? and deleted.empty?)
self end
Delayer.new do
Service.deach do |service|
fetch_and_modify_for_using_lists(service, true) end end
class IDs < TypedArray(Integer); end
end
|