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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +19 -0
- data/README.md +79 -4
- data/chatrix.gemspec +1 -6
- data/lib/chatrix.rb +1 -0
- data/lib/chatrix/api/api_component.rb +21 -0
- data/lib/chatrix/api/media.rb +67 -0
- data/lib/chatrix/api/push.rb +107 -0
- data/lib/chatrix/api/room_actions.rb +267 -0
- data/lib/chatrix/api/rooms.rb +213 -0
- data/lib/chatrix/api/session.rb +154 -0
- data/lib/chatrix/api/users.rb +176 -0
- data/lib/chatrix/client.rb +98 -0
- data/lib/chatrix/components/admin.rb +58 -0
- data/lib/chatrix/components/messaging.rb +42 -0
- data/lib/chatrix/components/permissions.rb +55 -0
- data/lib/chatrix/components/state.rb +160 -0
- data/lib/chatrix/components/timeline.rb +50 -0
- data/lib/chatrix/errors.rb +75 -7
- data/lib/chatrix/events.rb +39 -0
- data/lib/chatrix/matrix.rb +138 -595
- data/lib/chatrix/message.rb +53 -0
- data/lib/chatrix/room.rb +93 -0
- data/lib/chatrix/rooms.rb +90 -0
- data/lib/chatrix/user.rb +109 -0
- data/lib/chatrix/users.rb +74 -0
- data/lib/chatrix/version.rb +1 -1
- metadata +30 -81
@@ -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
|