-
Notifications
You must be signed in to change notification settings - Fork 24
/
goodbye_chatwork.rb
168 lines (154 loc) · 5.1 KB
/
goodbye_chatwork.rb
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
require "goodbye_chatwork/version"
require 'csv'
require 'fileutils'
require 'open-uri'
require 'json'
require 'faraday'
require 'faraday-cookie_jar'
module GoodbyeChatwork
class Client
REQUEST_INTERVAL = 1
CHAT_SIZE = 40
def initialize(id, pw, opt = {})
@verbose = opt[:verbose]
@id = id
@pw = pw
@opt = opt
@interval = opt[:inverval] || REQUEST_INTERVAL
@base_url = opt[:base_url] || 'https://www.chatwork.com'
@token = nil
@client = Faraday.new @base_url do |builder|
builder.request :url_encoded
builder.use :cookie_jar
builder.adapter Faraday.default_adapter
end
end
def login
login_r = @client.post '/login.php', email: @id, password: @pw, autologin: 'on'
if login_r.env.status == 302
@client.url_prefix = URI.parse(login_r.env.response_headers[:location].match(/^https?(:\/\/[-_.!~*\'()a-zA-Z0-9;\:\@&=+\$,%#]+)/).to_s)
@client.get login_r.env.response_headers[:location]
end
r = @client.get "/"
self.wait
self.info "login as #{@id} ..."
@token = r.body.match(/var ACCESS_TOKEN *= *'(.+)'/).to_a[1]
@myid = r.body.match(/var MYID *= *'(.+)'/).to_a[1]
raise 'no token' unless @token
self.init_load
self
end
def init_load
self.info "load initial data..."
r = @client.get "/gateway.php?cmd=init_load&myid=#{@myid}&_v=1.80a&_av=5&_t=#{@token}&ln=ja&rid=0&type=&new=1"
self.wait
d = JSON.parse(r.body)['result']
@contacts = d['contact_dat']
@rooms = d['room_dat']
end
def old_chat room_id, first_chat_id = 0
self.info "get old chat #{first_chat_id}- ..."
begin
res = @client.get do |req|
req.url "#{@client.url_prefix.to_s}gateway.php?cmd=load_old_chat&myid=#{@myid}&_v=1.80a&_av=5&_t=#{@token}&ln=ja&room_id=#{room_id}&last_chat_id=0&first_chat_id=#{first_chat_id}&jump_to_chat_id=0&unread_num=0&file=1&desc=1"
req.options.timeout = 5 # open/read timeout in seconds
req.options.open_timeout = 1000 # connection open timeout in seconds
end
rescue Faraday::ConnectionFailed => e
retry_count ||= 0
retry_count += 1
if retry_count < 5
retry
else
raise e
end
end
self.wait
r = JSON.parse(res.body)
r['result']['chat_list'].sort_by { |i| i['id'].to_i }.reverse
end
def account(aid)
@contacts[aid.to_s]
end
def file_download(file_id, opt = {})
info = self.file_info file_id
if !info[:url] || !info[:filename]
self.info "download error #{file_id}"
return
end
d = open(info[:url]).read
self.info "download #{info[:filename]} ..."
fn = "#{file_id}_#{info[:filename]}".force_encoding('utf-8')
out = fn
if opt[:dir]
unless File.exists?(opt[:dir])
self.info "mkdir #{opt[:dir]}"
FileUtils.mkdir_p opt[:dir]
end
out = File.join(opt[:dir], fn)
end
open(out, 'wb') { |f| f.write d }
end
def file_info(file_id)
self.info "get file info #{file_id} ..."
r = @client.get do |req|
req.url "#{@client.url_prefix.to_s}gateway.php?cmd=download_file&bin=1&file_id=#{file_id}"
req.options.timeout = 5 # open/read timeout in seconds
req.options.open_timeout = 10 # connection open timeout in seconds
end
self.wait
b = r.headers['Content-disposition'].match(/filename="=\?UTF-8\?B\?(.+)\?="/).to_a[1]
{ url: r.headers['Location'], filename: b.unpack('m')[0] }
end
def export_csv(room_id, out, opt = {})
self.info "export logs #{room_id} ..."
CSV.open(out, "wb") do |csv|
fid = 0
loop do
r = self.old_chat(room_id, fid)
r.each do |i|
if opt[:include_file]
fid = i['msg'].match(/\[download\:([^\]]+)\]/).to_a[1]
if fid
begin
file_download fid, dir: opt[:dir]
rescue Exception => e
self.info "download error #{fid} #{e.to_s}"
end
end
end
ac = self.account(i['aid'])
csv << [Time.at(i['tm']).iso8601,
(ac ? ac['name'] : i['aid']), i['msg']]
end
break if r.size < CHAT_SIZE
fid = r.last['id']
end
end
self.info "create #{out}"
end
def room_list
@rooms.to_a.sort_by { |i| i[0].to_i }.map do |i|
name = i[1]['n']
member_ids = i[1]['m'].keys
c = i[1]['c']
if !name && member_ids.size == 1 && member_ids.first == @myid
[i[0], 'mychat', c]
elsif name
[i[0], name, c]
elsif member_ids.size == 2 && member_ids.include?(@myid)
ac = self.account(member_ids.find { |i| i != @myid }) || {}
[i[0], ac['name'], c]
else
[i[0], '...', c]
end
end
end
def info(d)
STDERR.puts([Time.now.iso8601, d].flatten.join(' ')) if @verbose
end
def wait
sleep(@interval + rand)
end
end
end