chatrix 1.0.0.pre → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,98 @@
1
+ require 'chatrix/matrix'
2
+ require 'chatrix/users'
3
+ require 'chatrix/rooms'
4
+
5
+ require 'wisper'
6
+
7
+ module Chatrix
8
+ # A client wrapping the API in easy-to-use methods.
9
+ class Client
10
+ include Wisper::Publisher
11
+
12
+ # @!attribute [r] me
13
+ # @return [User] The user associated with the access token.
14
+ attr_reader :me
15
+
16
+ # Initializes a new Client instance.
17
+ #
18
+ # Currently it requires a token, future versions will allow login
19
+ # with arbitrary details.
20
+ #
21
+ # @param token [String] The access token to use.
22
+ # @param id [String] The user ID of the token owner.
23
+ # @param homeserver [String,nil] Homeserver to connect to. If not set,
24
+ # the default homeserver defined in Chatrix::Matrix will be used.
25
+ def initialize(token, id, homeserver: nil)
26
+ @matrix = Matrix.new token, homeserver
27
+
28
+ @users = Users.new
29
+ @rooms = Rooms.new @users, @matrix
30
+
31
+ @me = @users.send(:get_user, id)
32
+
33
+ @rooms.on(:added) do |room|
34
+ broadcast(:room_added, room)
35
+ room.timeline.on(:message) { |r, m| broadcast(:room_message, r, m) }
36
+ end
37
+ end
38
+
39
+ # Starts syncing against the homeserver.
40
+ #
41
+ # Launches a new thread that will continously check for new events
42
+ # from the server.
43
+ #
44
+ # @see #sync! See the documentation for {#sync!} for more information
45
+ # and what happens in case of an error during sync.
46
+ def start_syncing
47
+ @sync_thread ||= Thread.new { loop { sync! } }
48
+ end
49
+
50
+ # Stops syncing against the homeserver.
51
+ def stop_syncing
52
+ return unless @sync_thread.is_a? Thread
53
+ @sync_thread.exit
54
+ @sync_thread.join
55
+ @sync_thread = nil
56
+ end
57
+
58
+ # Gets the user with the specified ID or display name.
59
+ #
60
+ # @return [User,nil] Returns a User object if the user could be found,
61
+ # otherwise `nil`.
62
+ def get_user(id)
63
+ @users[id]
64
+ end
65
+
66
+ # Gets the room with the specified ID, alias, or name.
67
+ #
68
+ # @return [Room,nil] Returns a Room object if the room could be found,
69
+ # otherwise `nil`.
70
+ def get_room(id)
71
+ @rooms[id]
72
+ end
73
+
74
+ private
75
+
76
+ # Syncs against the server.
77
+ #
78
+ # If an API error occurs during sync, it will be rescued and broadcasted
79
+ # as `:sync_error`.
80
+ def sync!
81
+ events = @matrix.sync since: @since
82
+ process_sync events
83
+ rescue ApiError => err
84
+ broadcast(:sync_error, err)
85
+ end
86
+
87
+ # Process the sync result.
88
+ #
89
+ # @param events [Hash] The events to sync.
90
+ def process_sync(events)
91
+ return unless events.is_a? Hash
92
+ @since = events['next_batch']
93
+ broadcast(:sync, events)
94
+
95
+ @rooms.process_events events['rooms'] if events.key? 'rooms'
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,58 @@
1
+ require 'chatrix/user'
2
+
3
+ module Chatrix
4
+ module Components
5
+ # Provides administrative actions for a room.
6
+ class Admin
7
+ # Initializes a new Admin instance.
8
+ #
9
+ # @param room [Room] The room to administrate.
10
+ # @param matrix [Matrix] Matrix API instance.
11
+ def initialize(room, matrix)
12
+ @room = room
13
+ @matrix = matrix
14
+ end
15
+
16
+ # Joins the room. Can only be used on public rooms or if the user
17
+ # has been invited.
18
+ def join
19
+ @matrix.rooms.actions.join @room.id
20
+ end
21
+
22
+ # Leaves the room. If the user is currently invited to the room,
23
+ # leaving the room is the same as rejecting the invite.
24
+ def leave
25
+ @matrix.rooms.actions.leave @room.id
26
+ end
27
+
28
+ # Kicks a user from the room.
29
+ #
30
+ # @param user [User,String] The user to kick, can be either a User
31
+ # object or a String (user ID).
32
+ # @param reason [String] The reason for the kick.
33
+ # @return [Boolean] `true` if the user was kicked, otherwise `false`.
34
+ def kick(user, reason)
35
+ @matrix.rooms.actions.kick @room.id, user, reason
36
+ end
37
+
38
+ # Bans a user from the room.
39
+ #
40
+ # @param user [User,String] The user to kick, can be either a User
41
+ # object or a String (user ID).
42
+ # @param reason [String] The reason for the ban.
43
+ # @return [Boolean] `true` if the user was kicked, otherwise `false`.
44
+ def ban(user, reason)
45
+ @matrix.rooms.actions.ban @room.id, user, reason
46
+ end
47
+
48
+ # Unbans a user from the room.
49
+ #
50
+ # @param user [User,String] The user to unban, can be either a User
51
+ # objec or a String (user ID).
52
+ # @return [Boolean] `true` if the user was unbanned, otherwise `false`.
53
+ def unban(user)
54
+ @matrix.rooms.actions.unban @room.id, user
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,42 @@
1
+ module Chatrix
2
+ module Components
3
+ # Class to handle messaging actions for a room.
4
+ class Messaging
5
+ # Initializes a new Messaging instance.
6
+ # @param room [Room] The room to handle messaging for.
7
+ # @param matrix [Matrix] Matrix API instance.
8
+ def initialize(room, matrix)
9
+ @room = room
10
+ @matrix = matrix
11
+ end
12
+
13
+ # Sends a message to the room.
14
+ # @param message [String] The message to send.
15
+ # @return [String] Event ID for the send action.
16
+ def send_message(message)
17
+ @matrix.rooms.actions.send_message @room.id, message
18
+ end
19
+
20
+ # Sends a notice to the room.
21
+ # @param message [String] The notice to send.
22
+ # @return (see #send_message)
23
+ def send_notice(message)
24
+ @matrix.rooms.actions.send_message @room.id, message, 'm.notice'
25
+ end
26
+
27
+ # Sends an emote to the room.
28
+ # @param message [String] The emote text to send.
29
+ # @return (see #send_message)
30
+ def send_emote(message)
31
+ @matrix.rooms.actions.send_message @room.id, message, 'm.emote'
32
+ end
33
+
34
+ # Sends an HTML message to the room.
35
+ # @param message [String] The HTML formatted message to send.
36
+ # @return (see #send_message)
37
+ def send_html(message)
38
+ @matrix.rooms.actions.send_html @room.id, message
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,55 @@
1
+ require 'wisper'
2
+
3
+ module Chatrix
4
+ module Components
5
+ # Helper for parsing permissions in a room.
6
+ class Permissions
7
+ include Wisper::Publisher
8
+
9
+ # Initializes a new Permissions instance.
10
+ # @param room [Room] The room that this permissions set belongs to.
11
+ def initialize(room)
12
+ @room = room
13
+ @actions = {}
14
+ @events = {}
15
+ end
16
+
17
+ # Updates permission data.
18
+ # @param content [Hash] New permission data.
19
+ def update(content)
20
+ @actions[:ban] = content['ban']
21
+ @actions[:kick] = content['kick']
22
+ @actions[:invite] = content['invite']
23
+ @actions[:redact] = content['redact']
24
+
25
+ content['events'].each do |event, level|
26
+ @events[event.match(/\w+$/).to_s.to_sym] = level
27
+ end
28
+
29
+ broadcast :update, @room, self
30
+ end
31
+
32
+ # Check if a user can perform an action.
33
+ #
34
+ # @param user [User] The user to test.
35
+ # @param action [Symbol] The action to check.
36
+ # @return [Boolean] `true` if the user can perform the action,
37
+ # otherwise `false`.
38
+ def can?(user, action)
39
+ return false unless @actions.key? action
40
+ user.power_in(@room) >= @actions[action]
41
+ end
42
+
43
+ # Check if a user can set an event.
44
+ #
45
+ # @param user [User] The user to test.
46
+ # @param event [Symbol] The event to check.
47
+ # @return [Boolean] `true` if the user can set the event,
48
+ # otherwise `false`.
49
+ def can_set?(user, event)
50
+ return false unless @events.key? event
51
+ user.power_in(@room) >= @events[event]
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,160 @@
1
+ require 'chatrix/events'
2
+
3
+ require 'chatrix/components/permissions'
4
+
5
+ require 'set'
6
+ require 'wisper'
7
+
8
+ module Chatrix
9
+ module Components
10
+ # Manages state for a room.
11
+ class State
12
+ include Wisper::Publisher
13
+
14
+ # @!attribute [r] canonical_alias
15
+ # @return [String,nil] The canonical alias, or `nil` if none has
16
+ # been set.
17
+ # @!attribute [r] name
18
+ # @return [String,nil] The name, or `nil` if none has been set.
19
+ # @!attribute [r] topic
20
+ # @return [String,nil] The topic, or `nil` if none has been set.
21
+ # @!attribute [r] creator
22
+ # @return [User] The user who created the room.
23
+ # @!attribute [r] guest_access
24
+ # @return [Boolean] `true` if guests are allowed in the room,
25
+ # otherwise `false`.
26
+ # @!attribute [r] history_visibility
27
+ # @return [String] The room's history visibility.
28
+ # @!attribute [r] join_rule
29
+ # @return [String] Join rules for the room.
30
+ # @!attribute [r] permissions
31
+ # @return [Permissions] Check room permissions.
32
+ attr_reader :canonical_alias, :name, :topic, :creator, :guest_access,
33
+ :join_rule, :history_visibility, :permissions
34
+
35
+ # Initializes a new State instance.
36
+ #
37
+ # @param room [Room] The room the state belongs to.
38
+ # @param users [Users] The user manager.
39
+ def initialize(room, users)
40
+ @room = room
41
+ @users = users
42
+
43
+ @permissions = Permissions.new @room
44
+
45
+ @aliases = []
46
+ @members = Set.new
47
+ end
48
+
49
+ # Returns whether the specified user is a member of the room.
50
+ #
51
+ # @param user [User] The user to check.
52
+ # @return [Boolean] `true` if the user is a member of the room,
53
+ # otherwise `false`.
54
+ def member?(user)
55
+ @members.member? user
56
+ end
57
+
58
+ # Updates the state with new event data.
59
+ # @param data [Hash] Event data.
60
+ def update(data)
61
+ data['events'].each { |e| process_event e } if data.key? 'events'
62
+ end
63
+
64
+ private
65
+
66
+ # Processes a state event.
67
+ # @param event [Hash] Event data.
68
+ def process_event(event)
69
+ return if Events.processed? event
70
+
71
+ name = 'handle_' + event['type'].match(/\w+$/).to_s
72
+ send(name, event) if respond_to? name, true
73
+
74
+ Events.processed event
75
+ end
76
+
77
+ # Handle the `m.room.create` event.
78
+ # @param event [Hash] Event data.
79
+ def handle_create(event)
80
+ @creator = @users.send(:get_user, event['content']['creator'])
81
+ broadcast :creator, @room, @creator
82
+ end
83
+
84
+ # Handle the `m.room.canonical_alias` event.
85
+ # @param (see #handle_create)
86
+ def handle_canonical_alias(event)
87
+ @canonical_alias = event['content']['alias']
88
+ broadcast :canonical_alias, @room, @canonical_alias
89
+ end
90
+
91
+ # Handle the `m.room.aliases` event.
92
+ # @param (see #handle_create)
93
+ def handle_aliases(event)
94
+ @aliases.replace event['content']['aliases']
95
+ broadcast :aliases, @room, @aliases
96
+ end
97
+
98
+ # Handle the `m.room.name` event.
99
+ # @param (see #handle_create)
100
+ def handle_name(event)
101
+ broadcast :name, @room, @name = event['content']['name']
102
+ end
103
+
104
+ # Handle the `m.room.topic` event.
105
+ # @param (see #handle_create)
106
+ def handle_topic(event)
107
+ broadcast :topic, @room, @topic = event['content']['topic']
108
+ end
109
+
110
+ # Handle the `m.room.guest_access` event.
111
+ # @param (see #handle_create)
112
+ def handle_guest_access(event)
113
+ @guest_access = event['content']['guest_access'] == 'can_join'
114
+ broadcast :guest_access, @room, @guest_access
115
+ end
116
+
117
+ # Handle the `m.room.history_visibility` event.
118
+ # @param (see #handle_create)
119
+ def handle_history_visibility(event)
120
+ @history_visibility = event['content']['history_visibility']
121
+ broadcast :history_visibility, @room, @history_visibility
122
+ end
123
+
124
+ # Handle the `m.room.join_rules` event.
125
+ # @param (see #handle_create)
126
+ def handle_join_rules(event)
127
+ @join_rule = event['content']['join_rule']
128
+ broadcast :join_rule, @room, @join_rule
129
+ end
130
+
131
+ # Process a member event.
132
+ # @param event [Hash] The member event.
133
+ def handle_member(event)
134
+ @users.process_member_event self, event
135
+ user = @users[event['sender']]
136
+ membership = event['content']['membership'].to_sym
137
+
138
+ # Don't process invite state change if the user is already a
139
+ # member in the room.
140
+ return if membership == :invite && member?(user)
141
+
142
+ if membership == :join
143
+ @members.add user
144
+ else
145
+ @members.delete user
146
+ end
147
+
148
+ broadcast(membership, @room, user)
149
+ end
150
+
151
+ # Process a power level event.
152
+ # @param event [Hash] Event data.
153
+ def handle_power_levels(event)
154
+ content = event['content']
155
+ @permissions.update content
156
+ @users.process_power_levels @room, content['users']
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,50 @@
1
+ require 'chatrix/message'
2
+ require 'chatrix/events'
3
+
4
+ require 'wisper'
5
+
6
+ module Chatrix
7
+ module Components
8
+ # Manages the timeline for a room.
9
+ class Timeline
10
+ include Wisper::Publisher
11
+
12
+ # Initializes a new Timeline instance.
13
+ #
14
+ # @param room [Room] The room this timeline belongs to.
15
+ # @param users [Users] The user manager.
16
+ def initialize(room, users)
17
+ @room = room
18
+ @users = users
19
+ end
20
+
21
+ # Process timeline events.
22
+ # @param data [Hash] Events to process.
23
+ def update(data)
24
+ data['events'].each { |e| process_event e } if data.key? 'events'
25
+
26
+ # Pass the event data to state to handle any state updates
27
+ # in the timeline
28
+ @room.state.update data
29
+ end
30
+
31
+ private
32
+
33
+ # Processes a timeline event.
34
+ # @param event [Hash] Event data.
35
+ def process_event(event)
36
+ return if Events.processed? event
37
+ name = 'handle_' + event['type'].match(/\w+$/).to_s
38
+ send(name, event) if respond_to? name, true
39
+ end
40
+
41
+ # Process a message event.
42
+ # @param event [Hash] Event data.
43
+ def handle_message(event)
44
+ message = Message.new @users[event['sender']], event['content']
45
+ broadcast(:message, @room, message)
46
+ Events.processed event
47
+ end
48
+ end
49
+ end
50
+ end