root/lang/ruby/net-irc/trunk/examples/tig.rb @ 5531

Revision 5531, 7.8 kB (checked in by cho45, 5 years ago)

lang/ruby/net-irc/trunk/test/net-irc_test.rb,
lang/ruby/net-irc/trunk/lib/net/irc.rb,
lang/ruby/net-irc/trunk/examples,
lang/ruby/net-irc/trunk/examples/tig.rb,
lang/ruby/net-irc/trunk/examples/wig.rb,
lang/ruby/net-irc/trunk/examples/lig.rb:

Add tig.rb, wig.rb.
Fix some bugs...

  • Property svn:executable set to *
Line 
1#!/usr/bin/env ruby
2
3$LOAD_PATH << "lib"
4$LOAD_PATH << "../lib"
5
6require "rubygems"
7require "net/http"
8require "net/irc"
9require "uri"
10require "json"
11require "socket"
12require "time"
13require "logger"
14require "yaml"
15require "pathname"
16
17Net::HTTP.version_1_2
18
19class TwitterIrcGateway < Net::IRC::Server::Session
20        @@name     = "twittergw"
21        @@version  = "0.0.0"
22        @@channel  = "#twitter"
23        @@api_base = URI("http://twitter.com/")
24
25        class ApiFailed < StandardError; end
26
27        def initialize(*args)
28                super
29                @groups = {}
30                @channels = [] # join channels (groups)
31                @config = Pathname.new(ENV["HOME"]) + ".tig"
32                load_config
33        end
34
35        def on_user(m)
36                super
37                post @mask, JOIN, @@channel
38                @real, @opts = @real.split(/\s/)
39                @opts ||= []
40                @log.info "Client Options: #{@opts.inspect}"
41
42                @timeline = []
43                Thread.start do
44                        loop do
45                                begin
46                                        check_friends
47                                rescue ApiFailed => e
48                                        @log.error e.inspect
49                                rescue Exception => e
50                                        puts e
51                                        puts e.backtrace
52                                end
53                                sleep 10 * 60
54                        end
55                end
56                sleep 3
57                Thread.start do
58                        loop do
59                                begin
60                                        check_timeline
61                                        # check_direct_messages
62                                rescue ApiFailed => e
63                                        @log.error e.inspect
64                                rescue Exception => e
65                                        puts e
66                                        puts e.backtrace
67                                end
68                                sleep 90
69                        end
70                end
71        end
72
73        def on_privmsg(m)
74                retry_count = 3
75                ret = nil
76                target, message = *m.params
77                begin
78                        if target =~ /^#/
79                                ret = api("statuses/update.json", {"status" => message})
80                        else
81                                # direct message
82                                ret = api("direct_messages/new.json", {"user" => target, "text" => message})
83                        end
84                        raise ApiFailed, "api failed" unless ret
85                        log "Status Updated"
86                rescue => e
87                        @log.error [retry_count, e.inspect].inspect
88                        if retry_count > 0
89                                retry_count -= 1
90                                @log.debug "Retry to setting status..."
91                                retry
92                        else
93                                log "Some Error Happened on Sending #{message}. #{e}"
94                        end
95                end
96        end
97
98        def on_whois(m)
99                nick = m.params[0]
100                f = (@friends || []).find {|i| i["screen_name"] == nick }
101                if f
102                        post nil, RPL_WHOISUSER,   nick, nick, nick, @@api_base.host, "*", NKF.nkf("-j", "#{f["name"]} / #{f["description"]}")
103                        post nil, RPL_WHOISSERVER, nick, @@api_base.host, @@api_base.to_s
104                        post nil, RPL_WHOISIDLE,   nick, "0", "seconds idle"
105                        post nil, RPL_ENDOFWHOIS,  nick, "End of WHOIS list"
106                else
107                        post nil, ERR_NOSUCHNICK, nick, "No such nick/channel"
108                end
109        end
110
111        def on_who(m)
112                channel = m.params[0]
113                case
114                when channel == @@channel
115                        #     "<channel> <user> <host> <server> <nick>
116                        #         ( "H" / "G" > ["*"] [ ( "@" / "+" ) ]
117                        #             :<hopcount> <real name>"
118                        @friends.each do |f|
119                                user = nick = f["screen_name"]
120                                host = serv = @@api_base.host
121                                real = f["name"]
122                                post nil, RPL_WHOREPLY, channel, user, host, serv, nick, "H", "0 #{real}"
123                        end
124                        post nil, RPL_ENDOFWHO, nil, channel
125                when @groups.key?(channel)
126                        @groups[channel].each do |name|
127                                f = @friends.find {|i| i["screen_name"] == name }
128                                user = nick = f["screen_name"]
129                                host = serv = @@api_base.host
130                                real = f["name"]
131                                post nil, RPL_WHOREPLY, nil, channel, user, host, serv, nick, "H", "0 #{real}"
132                        end
133                        post nil, RPL_ENDOFWHO, nil, channel
134                else
135                        post nil, ERR_NOSUCHNICK, nick, "No such nick/channel"
136                end
137        end
138
139        def on_join(m)
140                channels = m.params[0].split(/\s*,\s*/)
141                channels.each do |channel|
142                        next if channel == @@channel
143
144                        @channels << channel
145                        @channels.uniq!
146                        post "#{@nick}!#{@nick}@#{@@api_base.host}", JOIN, channel
147                        save_config
148                end
149        end
150
151        def on_part(m)
152                channel = m.params[0]
153                return if channel == @@channel
154
155                @channels.delete(channel)
156                post @nick, "PART", channel, "Ignore group #{channel}, but setting is alive yet."
157        end
158
159        def on_invite(m)
160                nick, channel = *m.params
161                return if channel == @@channel
162
163                if (@friends || []).find {|i| i["screen_name"] == nick }
164                        ((@groups[channel] ||= []) << nick).uniq!
165                        post "#{nick}!#{nick}@#{@@api_base.host}", JOIN, channel
166                        save_config
167                else
168                        post ERR_NOSUCHNICK, nil, nick, "No such nick/channel"
169                end
170        end
171
172        def on_kick(m)
173                channel, nick, mes = *m.params
174                return if channel == @@channel
175
176                if (@friends || []).find {|i| i["screen_name"] == nick }
177                        (@groups[channel] ||= []).delete(nick)
178                        post nick, "PART", channel
179                        save_config
180                else
181                        post ERR_NOSUCHNICK, nil, nick, "No such nick/channel"
182                end
183        end
184
185        private
186        def check_timeline
187                first = true unless @prev_time
188                @prev_time = Time.at(0) if first
189                api("statuses/friends_timeline.json", {"since" => [@prev_time.httpdate] }).reverse_each do |s|
190                        nick = s["user"]["screen_name"]
191                        mesg = s["text"]
192                        time = Time.parse(s["created_at"]) rescue Time.now
193                        m = { "&quot;" => "\"", "&lt;"=> "<", "&gt;"=> ">", "&amp;"=> "&", "\n" => " "}
194                        mesg.gsub!(/(#{m.keys.join("|")})/) { m[$1] }
195                        @log.debug [nick, mesg, time].inspect
196                        if nick == @nick # 自分のときは topic に
197                                post nick, TOPIC, @@channel, mesg
198                        else
199                                message(nick, @@channel, mesg)
200                        end
201                        @groups.each do |channel,members|
202                                if members.include?(nick)
203                                        message(nick, channel, mesg)
204                                end
205                        end
206                end
207                @prev_time = Time.now
208        end
209
210        def check_direct_messages
211                first = true unless @prev_time_d
212                @prev_time_d = Time.now if first
213                api("direct_messages.json", {"since" => [@prev_time_d.httpdate] }).reverse_each do |s|
214                        nick = s["sender_screen_name"]
215                        mesg = s["text"]
216                        time = Time.parse(s["created_at"])
217                        @log.debug [nick, mesg, time].inspect
218                        message(nick, @nick, mesg)
219                end
220                @prev_time_d = Time.now
221        end
222
223        def check_friends
224                first = true unless @friends
225                @friends ||= []
226                friends = api("statuses/friends.json")
227                if first && !@opts.include?("athack")
228                        @friends = friends
229                        post nil, RPL_NAMREPLY,   @@name, @nick, "=", @@channel, @friends.map{|i| i["screen_name"] }.join(" ")
230                        post nil, RPL_ENDOFNAMES, @@name, @nick, @@channel, "End of NAMES list"
231                else
232                        prv_friends = @friends.map {|i| i["screen_name"] }
233                        now_friends = friends.map {|i| i["screen_name"] }
234                        (now_friends - prv_friends).each do |join|
235                                join = "@#{join}" if @opts.include?("athack")
236                                post "#{join}!#{join}@#{@@api_base.host}", JOIN, @@channel
237                        end
238                        (prv_friends - now_friends).each do |part|
239                                part = "@#{part}" if @opts.include?("athack")
240                                post "#{part}!#{part}@#{@@api_base.host}", PART, @@channel, ""
241                        end
242                        @friends = friends
243                end
244        end
245
246        def save_config
247                config = {
248                        :channels => @channels,
249                        :groups => @groups,
250                }
251                @config.open("w") do |f|
252                        YAML.dump(config, f)
253                end
254        end
255
256        def load_config
257                @config.open do |f|
258                        config = YAML.load(f)
259                        @channels = config[:channels]
260                        @groups   = config[:groups]
261                end
262        rescue Errno::ENOENT
263        end
264
265        def api(path, q={})
266                ret = {}
267                q["source"] = "tigrb"
268                q = q.inject([]) {|r,(k,v)| v.inject(r) {|r,i| r << "#{k}=#{URI.escape(i, /./)}" } }.join("&")
269                uri = @@api_base + "/#{path}?#{q}"
270                @log.debug uri.inspect
271                Net::HTTP.start(uri.host, uri.port) do |http|
272                        header = {
273                                'Authorization' => "Basic " + ["#{@real}:#{@pass}"].pack("m"),
274                        }
275                        case path
276                        when "statuses/update.json", "direct_messages/new.json"
277                                ret = http.post(uri.request_uri, q, header)
278                        else
279                                ret = http.get(uri.request_uri, header)
280                        end
281                end
282                @log.debug ret.inspect
283                case ret.code
284                when "200"
285                        JSON.parse(ret.body)
286                when "304"
287                        []
288                else
289                        raise ApiFailed, "Server Returned #{ret.code}"
290                end
291        rescue Errno::ETIMEDOUT, JSON::ParserError, IOError, Timeout::Error, Errno::ECONNRESET => e
292                raise ApiFailed, e.inspect
293        end
294
295        def message(sender, target, str)
296#                       str.gsub!(/&#(x)?([0-9a-f]+);/i) do |m|
297#                               [$1 ? $2.hex : $2.to_i].pack("U")
298#                       end
299                str = untinyurl(str)
300                sender =  "#{sender}!#{sender}@#{@@api_base.host}"
301                post sender, PRIVMSG, target, str
302        end
303
304        def log(str)
305                str.gsub!(/\n/, " ")
306                post @@name, NOTICE, @@channel, str
307        end
308
309        def untinyurl(text)
310                text.gsub(%r|http://tinyurl.com/[0-9a-z=]+|i) {|m|
311                        uri = URI(m)
312                        Net::HTTP.start(uri.host, uri.port) {|http|
313                                http.head(uri.request_uri)["Location"]
314                        }
315                }
316        end
317end
318
319if __FILE__ == $0
320        Net::IRC::Server.new("localhost", 16668, TwitterIrcGateway).start
321end
322
323
324
Note: See TracBrowser for help on using the browser.