4 gem('twitter4r', '>=0.2.1')
8 CONN_MGR_PARAM_FLAG_REQUIRED = 1
9 CONN_MGR_PARAM_FLAG_REGISTER = 2
10 CONN_MGR_PARAM_FLAG_HAS_DEFAULT = 4
11 CONN_MGR_PARAM_FLAG_SECRET = 8
14 HANDLE_TYPE_CONTACT = 1
19 CONNECTION_STATUS_CONNECTED = 0
20 CONNECTION_STATUS_CONNECTING = 1
21 CONNECTION_STATUS_DISCONNECTED = 2
23 CONNECTION_STATUS_REASON_NONE_SPECIFIED = 0
24 CONNECTION_STATUS_REASON_REQUESTED = 1
25 CONNECTION_STATUS_REASON_NETWORK_ERROR = 2
26 CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED = 3
27 CONNECTION_STATUS_REASON_ENCRYPTION_ERROR = 4
28 CONNECTION_STATUS_REASON_NAME_IN_USE = 5
29 CONNECTION_STATUS_REASON_CERT_NOT_PROVIDED = 6
30 CONNECTION_STATUS_REASON_CERT_UNTRUSTED = 7
31 CONNECTION_STATUS_REASON_CERT_EXPIRED = 8
32 CONNECTION_STATUS_REASON_CERT_NOT_ACTIVATED = 9
33 CONNECTION_STATUS_REASON_CERT_HOSTNAME_MISMATCH = 10
34 CONNECTION_STATUS_REASON_CERT_FINGERPRINT_MISMATCH = 11
35 CONNECTION_STATUS_REASON_CERT_SELF_SIGNED = 12
36 CONNECTION_STATUS_REASON_CERT_OTHER_ERROR = 13
38 # TODO: other flags in this set
39 CHANNEL_GROUP_FLAG_PROPERTIES = 2048
41 #Handles used for contact lists, of type HANDLE_TYPE_LIST
42 LIST_HANDLE_FOLLOWERS = 1
43 LIST_HANDLE_FRIENDS = 2
45 class Twitterpathy < DBus::Object
46 dbus_interface "org.freedesktop.Telepathy.ConnectionManager" do
47 dbus_method :GetParameters, "in proto:s, out parameters:a(susv)" do |proto|
48 puts "GetParameters(#{proto.inspect})"
49 $stderr.puts "Attempt to use unsupported protocol #{proto}" if proto != 'twitter'
51 parameters = [['account', CONN_MGR_PARAM_FLAG_REQUIRED | CONN_MGR_PARAM_FLAG_REGISTER, 's', ['s', '']], ['password', CONN_MGR_PARAM_FLAG_REQUIRED | CONN_MGR_PARAM_FLAG_REGISTER | CONN_MGR_PARAM_FLAG_SECRET, 's', ['s', '']]]
55 dbus_method :ListProtocols, "out protocols:as" do ||
57 protocols = ['twitter']
61 dbus_method :RequestConnection, "in proto:s, in parameters:a{sv}, out service:s, out object:o" do |proto, parameters|
62 puts "RequestConnection(#{proto.inspect}, #{parameters.inspect})"
63 $stderr.puts "Attempt to use unsupported protocol #{proto}" if proto != 'twitter'
65 object = "/org/freedesktop/Telepathy/Connection/Twitterpathy/twitter/#{parameters['account']}"
66 service = "org.freedesktop.Telepathy.Connection.Twitterpathy.twitter.#{parameters['account']}"
68 #Register a new D-BUS service for this connection
69 connection_service = $bus.request_service(service)
71 #Create the connection object, and export it on the new service
72 connection = TwitterConnection.new(object, parameters['account'], parameters['password'], connection_service)
73 connection_service.export(connection)
76 NewConnection(service, object, 'twitter')
81 dbus_signal :NewConnection, 'bus_name:s, object_path:o, proto:s'
85 class TwitterConnection < DBus::Object
86 attr_reader :me, :myfollowerhandles
88 def initialize(path, account, password, connection_service)
89 puts "initialize TwitterConnection(#{path}, #{account}, ***, #{connection_service})"
92 @connection_service = connection_service
93 @status = CONNECTION_STATUS_CONNECTING
94 @handles = {HANDLE_TYPE_NONE => {}, HANDLE_TYPE_CONTACT => {}, HANDLE_TYPE_ROOM => {}, HANDLE_TYPE_LIST => {}, HANDLE_TYPE_GROUP => {}}
98 dbus_interface 'org.freedesktop.Telepathy.Connection' do
99 dbus_method :Connect, '' do ||
100 puts "Connect, account=#{@account}"
102 @twitter_client = Twitter::Client.new(:login => @account, :password => @password)
104 @me = @twitter_client.my(:info)
105 @handles[HANDLE_TYPE_CONTACT][@me.id] = @me
107 puts "Connected as #{@me.inspect}"
110 myfollowers = @twitter_client.my(:followers)
111 @myfollowerhandles = myfollowers.map{|follower| follower.id }
112 myfollowers.each do |follower|
113 @handles[HANDLE_TYPE_CONTACT][follower.id] = follower
116 followers_channel_object = "/org/freedesktop/Telepathy/Connection/Twitterpathy/twitter/#{@account}/contactlist/followers"
117 followers_channel = FollowersChannel.new(followers_channel_object, self)
118 @connection_service.export(followers_channel)
120 @channels = [[followers_channel_object, 'org.freedesktop.Telepathy.Channel.Type.ContactList', HANDLE_TYPE_LIST, LIST_HANDLE_FOLLOWERS]]
121 @handles[HANDLE_TYPE_LIST][LIST_HANDLE_FOLLOWERS] = followers_channel
123 @status = CONNECTION_STATUS_CONNECTED
125 StatusChanged(@status, CONNECTION_STATUS_REASON_REQUESTED)
126 NewChannel(followers_channel_object, 'org.freedesktop.Telepathy.Channel.Type.ContactList', HANDLE_TYPE_LIST, LIST_HANDLE_FOLLOWERS, false)
129 dbus_method :Disconnect, '' do ||
132 @status = CONNECTION_STATUS_DISCONNECTED
134 StatusChanged(@status, CONNECTION_STATUS_REASON_REQUESTED)
137 dbus_method :GetInterfaces, 'out interfaces:as' do ||
142 dbus_method :GetProtocol, 'out protocol:s' do ||
147 dbus_method :GetSelfHandle, 'out self_handle:u' do ||
152 dbus_method :GetStatus, 'out status:u' do ||
156 dbus_method :HoldHandles, 'in handle_type:u, in handles:au' do |handle_type, handles|
160 dbus_method :InspectHandles, 'in handle_type:u, in handles:au, out strings:as' do |handle_type, handles|
161 strings = handles.map{|handle| @handles[handle_type][handle].inspect }
165 dbus_method :ListChannels, 'out channels:a(osuu)' do ||
169 dbus_method :ReleaseHandles, 'in handle_type:u, in handles:au' do |handle_type, handles|
173 dbus_method :RequestChannel, 'in type:s, in handle_type:u, in handle:u, in suppress_handler:b, out channel:o' do |type, handle_type, handle, suppress_handler|
174 if type == 'org.freedesktop.Telepathy.Channel.Type.ContactList' && handle_type == HANDLE_TYPE_LIST && handle == LIST_HANDLE_FOLLOWERS
175 channel = "/org/freedesktop/Telepathy/Connection/Twitterpathy/twitter/#{@account}/contactlist/followers"
177 # TODO: throw org.freedesktop.Telepathy.Error.NotImplemented or org.freedesktop.Telepathy.Error.InvalidHandle
183 dbus_method :RequestHandles, 'in handle_type:u, in names:as, out handles:au' do |handle_type, names|
184 puts "RequestHandles(#{handle_type.inspect}, #{names.inspect})"
186 if handle_type == HANDLE_TYPE_CONTACT
187 handles = names.map {|name|
188 # TODO: make this more efficient?
190 @handles[HANDLE_TYPE_CONTACT].each do |handle, contact|
191 if contact.name == name
192 contact_handle = handle
197 if contact_handle.nil?
198 contact = @twitter_client.user(name) # TODO: handle errors
199 @handles[HANDLE_TYPE_CONTACT][contact.id] = contact
200 contact_handle = contact.id
205 elsif handle_type = HANDLE_TYPE_LIST
206 handles = names.map {|name|
208 LIST_HANDLE_FOLLOWERS
222 dbus_signal :NewChannel, 'object_path:o, channel_type:s, handle_type:u, handle:u, suppress_handler:b'
223 dbus_signal :StatusChanged, 'status:u, reason:u'
227 class FollowersChannel < DBus::Object
228 def initialize(path, connection)
229 @connection = connection
231 'org.freedesktop.Telepathy.Channel' => {
232 'ChannelType' => ['s', 'org.freedesktop.Telepathy.Channel.Type.ContactList'],
233 'Interfaces' => ['as', ['org.freedesktop.Telepathy.Channel.Interface.Group']],
234 'TargetHandle' => ['u', LIST_HANDLE_FOLLOWERS],
235 'TargetID' => ['s', 'Followers'],
236 'TargetHandleType' => ['u', HANDLE_TYPE_LIST]
238 'org.freedesktop.Telepathy.Channel.Interface.Group' => {
239 'GroupFlags' => ['u', CHANNEL_GROUP_FLAG_PROPERTIES],
240 'HandleOwners' => ['a{uu}', {}],
241 'LocalPendingMembers' => ['a(uuus)', []],
242 'Members' => ['au', connection.myfollowerhandles],
243 'RemotePendingMembers' => ['au', []],
244 'SelfHandle' => ['u', connection.me.id]
250 dbus_interface 'org.freedesktop.Telepathy.Channel' do
251 dbus_method :Close, '' do ||
252 # TODO: throw org.freedesktop.Telepathy.Error.NotImplemented
255 #Deprecated methods, just return appropriate properties
256 dbus_method :GetChannelType, 'out ChannelType:s' do ||
257 [@properties['org.freedesktop.Telepathy.Channel']['ChannelType'][1]]
260 dbus_method :GetHandle, 'out TargetHandleType:u, out TargetHandle:u' do ||
261 [@properties['org.freedesktop.Telepathy.Channel']['TargetHandleType'][1], @properties['org.freedesktop.Telepathy.Channel']['TargetHandle'][1]]
264 dbus_method :GetInterfaces, 'out Interfaces:as' do ||
265 [@properties['org.freedesktop.Telepathy.Channel']['Interfaces'][1]]
268 dbus_signal :Closed, ''
271 # TODO: make a nice general way to add properties to classes
272 dbus_interface 'org.freedesktop.DBus.Properties' do
273 dbus_method :Get, 'in interface_name:s, in property_name:s, out value:v' do |interface_name, property_name|
274 value = @properties[interface_name][property_name]
278 dbus_method :Set, 'in interface_name:s, in property_name:s, in value:v' do |interface_name, property_name, value|
281 dbus_method :GetAll, 'in interface_name:s, out props:a{sv}' do |interface_name|
282 props = @properties[interface_name]
287 dbus_interface 'org.freedesktop.Telepathy.Channel.Type.ContactList' do
290 dbus_interface 'org.freedesktop.Telepathy.Channel.Interface.Group' do
291 dbus_method :AddMembers, 'in contacts:au, in message:s' do |contacts, message|
295 dbus_method :RemoveMembers, 'in contacts:au, in message:s' do |contacts, message|
299 dbus_method :RemoveMembersWithReason, 'in contacts:au, in message:s, in reason:u' do |contacts, message, reason|
304 dbus_method :GetGroupFlags, 'out GroupFlags:u' do ||
305 [@properties['org.freedesktop.Telepathy.Channel.Interface.Group']['GroupFlags'][1]]
308 dbus_method :GetLocalPendingMembers, 'out LocalPendingMembers:au' do ||
309 [@properties['org.freedesktop.Telepathy.Channel.Interface.Group']['LocalPendingMembers'][1].map{|member| member[0]}]
312 dbus_method :GetLocalPendingMembersWithInfo, 'out LocalPendingMembers:a(uuus)' do ||
313 [@properties['org.freedesktop.Telepathy.Channel.Interface.Group']['LocalPendingMembers'][1]]
316 dbus_method :GetMembers, 'out Members:au' do ||
317 puts "GetMembers, returning #{@properties['org.freedesktop.Telepathy.Channel.Interface.Group']['Members'][1].inspect}"
318 [@properties['org.freedesktop.Telepathy.Channel.Interface.Group']['Members'][1]]
321 dbus_method :GetRemotePendingMembers, 'out RemotePendingMembers:au' do ||
322 [@properties['org.freedesktop.Telepathy.Channel.Interface.Group']['RemotePendingMembers'][1]]
325 dbus_method :GetSelfHandle, 'out SelfHandle:u' do ||
326 [@properties['org.freedesktop.Telepathy.Channel.Interface.Group']['SelfHandle'][1]]
329 dbus_signal :HandleOwnersChanged, 'added:a{uu}, removed:au'
330 dbus_signal :SelfHandleChanged, 'self_handle:u'
331 dbus_signal :GroupFlagsChanged, 'added:u, removed:u'
332 dbus_signal :MembersChanged, 'message:s, added:au, removed:au, local_pending:au, remote_pending:au, actor:u, reason:u'
336 $bus = DBus::SessionBus.instance
338 service = $bus.request_service("org.freedesktop.Telepathy.ConnectionManager.Twitterpathy")
340 exported_obj = Twitterpathy.new("/org/freedesktop/Telepathy/ConnectionManager/Twitterpathy")
341 service.export(exported_obj)
344 main = DBus::Main.new