]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blob - data/rbot/plugins/time.rb
plugin(points): new message parser, see #34
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / time.rb
1 #-- vim:sw=2:et
2 #++
3 #
4 # :title: Time Zone Plugin for rbot
5 #
6 # Author:: Ian Monroe <ian@monroe.nu>
7 # Author:: Raine Virta <raine.virta@gmail.com>
8 # Copyright:: (C) 2006 Ian Monroe
9 # Copyright:: (C) 2010 Raine Virta
10 # License:: MIT license
11
12 require 'tzinfo'
13
14 class TimePlugin < Plugin
15   def help(plugin, topic="")
16     case topic
17     when "set"
18       _("usage: time set <Continent>/<City> -- setting your location allows the bot to calibrate time replies into your time zone, and other people to figure out what time it is for you")
19     else
20       _("usage: time <timestamp|time zone|nick> -- %{b}timestamp%{b}: get info about a specific time, relative to your own time zone | %{b}time zone%{b}: get local time of a certain location, <time zone> can be '<Continent>/<City>' or a two character country code | %{b}nick%{b}: get local time of another person, given they have set their location | see `%{prefix}help time set` on how to set your location") % {
21         :b => Bold,
22         :prefix => @bot.config['core.address_prefix'].first
23       }
24     end
25   end
26
27   def initialize
28     super
29     # this plugin only wants to store strings
30     class << @registry
31       def store(val)
32         val
33       end
34       def restore(val)
35         val
36       end
37     end
38   end
39
40   def getTime(m, zone )
41     if zone.length == 2 then #country code
42       zone.upcase!
43       zone = 'GB' if zone == 'UK' #country doesn't know its own name
44       begin
45         nationZones = TZInfo::Country.get(zone).zone_identifiers
46         if nationZones.size == 1 then
47           zone = nationZones[0]
48         else
49           m.reply "#{zone} has the cities of #{nationZones.join( ', ' )}."
50         end
51       rescue TZInfo::InvalidCountryCode
52         m.reply "#{zone} is not a valid country code."
53       end
54     end
55     ['/', '_'].each { |sp|
56         arr = Array.new
57         zone.split(sp).each{ |s|
58             s[0] = s[0,1].upcase
59             s[1, s.length] = s[1, s.length].downcase if sp == '/'
60             arr.push(s) }
61             zone = arr.join( sp )
62         }
63
64     tz = TZInfo::Timezone.get( zone )
65     "#{tz.friendly_identifier} - #{tz.now.strftime( '%a %b %d %H:%M' )} #{tz.current_period.abbreviation}"
66   end
67
68   def showTime(m, params)
69     nick = nil
70     zone = nil
71     speaker = false
72     case params[:where].size
73     when 0
74       nick = m.sourcenick
75       speaker = true
76     when 1
77       zone = params[:where].first
78       nick = m.channel.get_user(zone)
79       speaker = (nick == m.sourcenick)
80     else
81       zone = params[:where].join('_')
82     end
83
84     # now we have a non-nil nick iff we want user information
85     if nick
86       if @registry.has_key? nick
87         zone = @registry[nick]
88       else
89         if speaker
90           msg = _("I don't know where you are, use %{pfx}time set <Continent>/<City> to let me know")
91         else
92           msg = _("I don't know where %{nick} is, (s)he should use %{pfx}time set <Continent>/<City> to let me know")
93         end
94         m.reply(msg % { :pfx => @bot.config['core.address_prefix'].first, :nick => nick })
95         return false
96       end
97     end
98
99     begin
100       m.reply getTime( m,  zone )
101     rescue TZInfo::InvalidTimezoneIdentifier
102       parse(m, params)
103     end
104   end
105
106   def setUserZone( m, params )
107     if params[:where].size > 0 then
108       s = setZone( m, m.sourcenick, params[:where].join('_') )
109     else
110       m.reply "Requires <Continent>/<City> or country code"
111     end
112   end
113
114   def resetUserZone( m, params )
115     s = resetZone( m, m.sourcenick)
116   end
117
118   def setAdminZone( m, params )
119     if params[:who] and params[:where].size > 0 then
120       s = setZone( m, params[:who], params[:where].join('_') )
121     else
122       m.reply "Requires a nick and the <Continent>/<City> or country code"
123     end
124   end
125
126   def resetAdminZone( m, params )
127     if params[:who]
128       s = resetZone( m, params[:who])
129     else
130       m.reply "Requires a nick"
131     end
132   end
133
134   def setZone( m, user, zone )
135     begin
136       getTime( m,  zone )
137     rescue TZInfo::InvalidTimezoneIdentifier
138       m.reply "#{zone} is an invalid time zone. Format is <Continent>/<City> or a two character country code."
139       return
140     end
141     @registry[ user ] = zone
142     m.reply "Ok, I'll remember that #{user} is on the #{zone} time zone"
143   end
144
145   def resetZone( m, user )
146     @registry.delete(user)
147     m.reply "Ok, I've forgotten #{user}'s time zone"
148   end
149
150   def parse(m, params)
151     require 'time'
152     str = params[:where].to_s
153     now = Time.now
154
155     begin
156       time = begin
157         if zone = @registry[m.sourcenick]
158           on_timezone(zone) {
159             Time.parse str
160           }
161         else
162           Time.parse str
163         end
164       rescue ArgumentError => e
165         # Handle 28/9/1978, which is a valid date representation at least in Italy
166         if e.message == 'argument out of range'
167           str.tr!('/', '-')
168           Time.parse str
169         else
170           raise
171         end
172       end
173
174       offset = (time - now).abs
175       raise if offset < 0.1
176     rescue => e
177       if str.match(/^\d+$/)
178         time = Time.at(str.to_i)
179       else
180         m.reply _("unintelligible time")
181         return
182       end
183     end
184
185     if zone = @registry[m.sourcenick]
186       time = time.convert_zone(zone)
187     end
188
189     m.reply _("%{time} %{w} %{str}") % {
190       :time => time.strftime(_("%a, %d %b %Y %H:%M:%S %Z %z")),
191       :str  => Utils.timeago(time),
192       :w    => time >= now ? _("is") : _("was")
193     }
194   end
195
196   def on_timezone(to_zone)
197     original_zone = ENV["TZ"]
198     ENV["TZ"] = to_zone
199     ret = yield
200     ENV["TZ"] = original_zone
201     return ret
202   end
203 end
204
205 class ::Time
206   def convert_zone(to_zone)
207     original_zone = ENV["TZ"]
208     utc_time = dup.gmtime
209     ENV["TZ"] = to_zone
210     to_zone_time = utc_time.localtime
211     ENV["TZ"] = original_zone
212     return to_zone_time
213   end
214 end
215
216 plugin = TimePlugin.new
217
218 plugin.default_auth('admin', false)
219
220 plugin.map 'time set [time][zone] [to] *where', :action=> 'setUserZone', :defaults => {:where => false}
221 plugin.map 'time reset [time][zone]', :action=> 'resetUserZone'
222 plugin.map 'time admin set [time][zone] [for] :who [to] *where', :action=> 'setAdminZone', :defaults => {:who => false, :where => false}
223 plugin.map 'time admin reset [time][zone] [for] :who', :action=> 'resetAdminZone', :defaults => {:who => false}
224 plugin.map 'time *where', :action => 'showTime', :defaults => {:where => false}