File Coverage

blib/lib/Circle/Net/Matrix/Room.pm
Criterion Covered Total %
statement 24 132 18.1
branch 0 22 0.0
condition 0 18 0.0
subroutine 8 29 27.5
pod 0 18 0.0
total 32 219 14.6


line stmt bran cond sub pod time code
1             # You may distribute under the terms of the GNU General Public License
2             #
3             # (C) Paul Evans, 2014-2017 -- leonerd@leonerd.org.uk
4              
5             package Circle::Net::Matrix::Room;
6              
7 1     1   1396 use strict;
  1         1  
  1         27  
8 1     1   4 use warnings;
  1         0  
  1         30  
9 1     1   3 use base qw( Tangence::Object Circle::WindowItem );
  1         2  
  1         119  
10              
11             our $VERSION = '0.01';
12              
13 1     1   5 use Data::Dump qw( pp );
  1         1  
  1         45  
14 1     1   4 use Scalar::Util qw( weaken );
  1         1  
  1         957  
15              
16             # To allow for out-of-tree development, use an inline Tangence class
17             # declaration instead of a .tan file
18             #
19             # class Circle.Net.Matrix.Room {
20             # isa Circle.WindowItem;
21             #
22             # smashed prop name = str;
23             # smashed prop topic = str;
24             # }
25              
26             sub DECLARE_TANGENCE
27             {
28 0     0 0   Tangence::Class->declare( __PACKAGE__,
29             props => {
30             name => {
31             dim => Tangence::Constants::DIM_SCALAR,
32             type => 'str',
33             },
34             topic => {
35             dim => Tangence::Constants::DIM_SCALAR,
36             type => 'str',
37             },
38             },
39              
40             superclasses => [qw( Circle::WindowItem )],
41             );
42             }
43              
44             sub WEAKSELF_EVAL
45             {
46 0     0 0   my ( $self, $method ) = @_;
47 0 0   0     my $code = $self->can( $method ) or return sub {};
48              
49 0           weaken( $self );
50             return sub {
51 0     0     my @args = @_;
52 0 0         eval { $self->$code( @args ); 1 } or
  0            
  0            
53             warn $@;
54 0           };
55             }
56              
57 0     0 0   sub init_prop_topic { "" }
58              
59             sub new
60             {
61 0     0 0   my $class = shift;
62 0           my %args = @_;
63              
64 0           my $self = $class->SUPER::new( @_ );
65              
66 0           my $room = $self->{room} = $args{room};
67              
68 0           my $name = $room->name;
69              
70 0           $self->set_prop_name( $name );
71 0           $self->set_prop_tag( $name );
72              
73 0           $self->{root} = $args{root};
74 0           $self->{net} = $args{net};
75              
76 0           weaken( my $weakself = $self );
77 0           $room->configure(
78             on_synced_state => $self->WEAKSELF_EVAL( 'on_synced_state' ),
79              
80             on_message => $self->WEAKSELF_EVAL( 'on_message' ),
81             on_membership => $self->WEAKSELF_EVAL( 'on_membership' ),
82             on_state_changed => $self->WEAKSELF_EVAL( 'on_state_changed' ),
83             );
84              
85 0           return $self;
86             }
87              
88             # Convenience accessor
89             sub name
90             {
91 0     0 0   my $self = shift;
92 0           return $self->get_prop_name;
93             }
94              
95             sub enumerable_name
96             {
97 0     0 0   my $self = shift;
98 0           return $self->name;
99             }
100              
101             sub get_prop_tag
102             {
103 0     0 0   my $self = shift;
104 0           return $self->name;
105             }
106              
107             sub parent
108             {
109 0     0 0   my $self = shift;
110 0           return $self->{net};
111             }
112              
113             sub commandable_parent
114             {
115 0     0 0   my $self = shift;
116 0           return $self->parent;
117             }
118              
119             sub on_synced_state
120             {
121 0     0 0   my $self = shift;
122              
123 0           my $room = $self->{room};
124              
125             # Since we now know the true name
126 0           $self->set_prop_name( $room->name );
127 0           $self->set_prop_tag( $room->name );
128              
129 0           $self->set_prop_topic( $room->topic );
130             }
131              
132             sub on_message
133             {
134 0     0 0   my $self = shift; shift;
  0            
135 0           my ( $member, $content, $event ) = @_;
136 0           my $member_id = $member->user->user_id;
137              
138 0           my $tstamp = $event->{origin_server_ts} / 1000;
139 0           my $type = $content->{msgtype};
140              
141 0           my ( $etype, $args );
142 0 0         if( $type eq "m.text" ) {
    0          
143             ( $etype, $args ) = ( "matrix.text" => {
144             name => $member->displayname,
145             user_id => $member_id,
146             text => $content->{body},
147 0           });
148             }
149             elsif( $type eq "m.emote" ) {
150             ( $etype, $args ) = ( "matrix.emote" => {
151             name => $member->displayname,
152             user_id => $member_id,
153             text => $content->{body},
154 0           });
155             }
156             else {
157 0           ( $etype, $args ) = ( "text" => {
158             text => "Unrecognised Matrix event msgtype <$type>"
159             });
160             }
161              
162 0           $self->push_displayevent( $etype, $args, time => $tstamp );
163 0           $self->bump_level( 2 );
164             }
165              
166             sub on_membership
167             {
168 0     0 0   my $self = shift; shift;
  0            
169 0           my ( $member, $event, $subject, %changes ) = @_;
170 0           my $member_id = $member->user->user_id;
171              
172 0           my $tstamp = $event->{origin_server_ts} / 1000;
173              
174 0 0         if( my $membership = $changes{membership} ) {
175 0 0 0       if( !defined $membership->[0] and $membership->[1] eq "join" ) {
176 0   0       $self->push_displayevent( "matrix.join" => {
177             name => $member->displayname // "[$member_id]",
178             user_id => $member_id,
179             }, time => $tstamp );
180 0           $self->bump_level( 1 );
181             return
182 0           }
183 0 0 0       if( $membership->[0] eq "join" and !defined $membership->[1] ) {
184 0   0       $self->push_displayevent( "matrix.leave" => {
185             name => $member->displayname // "[$member_id]",
186             user_id => $member_id,
187             });
188 0           $self->bump_level( 1 );
189             return
190 0           }
191             }
192              
193 0 0         if( my $displayname = $changes{displayname} ) {
    0          
    0          
194 0           $self->push_displayevent( "matrix.rename" => {
195             oldname => $displayname->[0],
196             newname => $displayname->[1],
197             user_id => $member_id,
198             }, time => $tstamp );
199 0           $self->bump_level( 1 );
200             }
201             elsif( my $state = $changes{state} ) {
202 0   0       my $message = $changes{status_msg} && $changes{status_msg}[1];
203              
204 0 0 0       $self->push_displayevent( "matrix.state" => {
205             state => $state->[1],
206             message => ( defined $message ? "($message)" : "" ),
207             name => $member->displayname // "[$member_id]",
208             }, time => $tstamp );
209 0           $self->bump_level( 1 );
210             }
211             elsif( keys %changes ) { # ignore "empty" changes e.g. avatar_url
212             # TODO for debugging
213 0           $self->push_displayevent( text => {
214 0           text => "Member $member changed to ${\pp \%changes}"
215             }, time => $tstamp );
216              
217 0           $self->bump_level( 2 );
218             }
219             }
220              
221             sub on_state_changed
222             {
223 0     0 0   my $self = shift; shift;
  0            
224 0           my ( $member, $event, %changes ) = @_;
225 0           my $member_id = $member->user->user_id;
226              
227 0           my $tstamp = $event->{origin_server_ts} / 1000;
228              
229 0           if( 1 ) {
230             # TODO for debugging
231 0           $self->push_displayevent( text => {
232 0           text => "Member $member changed room state ${\pp \%changes}"
233             }, time => $tstamp );
234             }
235             }
236              
237             sub enter_text
238             {
239 0     0 0   my $self = shift;
240 0           my ( $text ) = @_;
241              
242 0           my $room = $self->{room};
243 0           my $f = $room->send_message( $text );
244              
245 0           $room->adopt_future( $f );
246             }
247              
248             sub command_leave
249             : Command_description("Leave the room")
250             {
251 0     0 0 0 my $self = shift;
252 0         0 my ( $cinv ) = @_;
253              
254 0         0 my $matrix = $self->{net}{matrix};
255              
256 0         0 $matrix->leave_room( $self->{room}->room_id );
257              
258 0         0 return;
259 1     1   5 }
  1         1  
  1         5  
260              
261             sub command_say
262             : Command_description("Quote text directly as a text message")
263             : Command_arg('text', eatall => 1)
264             {
265 0     0 0 0 my $self = shift;
266 0         0 my ( $text ) = @_;
267              
268 0         0 $self->enter_text( $text );
269              
270 0         0 return;
271 1     1   292 }
  1         1  
  1         5  
272              
273             sub command_me
274             : Command_description("Send an emote message")
275             : Command_arg('text', eatall => 1)
276             {
277 0     0 0   my $self = shift;
278 0           my ( $text ) = @_;
279              
280 0           my $room = $self->{room};
281 0           my $f = $room->send_message( type => "m.emote", body => $text );
282              
283 0           $room->adopt_future( $f );
284              
285 0           return;
286 1     1   374 }
  1         1  
  1         3  
287              
288             sub make_widget_pre_scroller
289             {
290 0     0 0   my $self = shift;
291 0           my ( $box ) = @_;
292              
293 0           my $registry = $self->{registry};
294              
295 0           my $topicentry = $registry->construct(
296             "Circle::Widget::Entry",
297             classes => [qw( topic )],
298             # on_enter => sub { $self->topic( $_[0] ) },
299             );
300             $self->watch_property( "topic",
301 0     0     on_updated => sub { $topicentry->set_prop_text( $_[1] ) }
302 0           );
303              
304 0           $box->add( $topicentry );
305             }
306              
307             0x55AA;