chatrix 1.0.0.pre → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|