diff --git a/IRCShell.pm b/IRCShell.pm new file mode 100755 index 0000000..013a9c3 --- /dev/null +++ b/IRCShell.pm @@ -0,0 +1,308 @@ +package Mojo::Weixin::Plugin::IRCShell; +$Mojo::Weixin::Plugin::IRCShell::PRIORITY = 99; +use strict; +use List::Util qw(first); +BEGIN{ + $Mojo::Weixin::Plugin::IRCShell::has_mojo_irc_server = 0; + eval{ + require Mojo::IRC::Server::Chinese; + }; + $Mojo::Weixin::Plugin::IRCShell::has_mojo_irc_server = 1 if not $@; +} + +my $ircd; +sub call{ + my $client = shift; + my $data = shift; + $client->die("请先安装模块 Mojo::IRC::Server::Chinese") if not $Mojo::Weixin::Plugin::IRCShell::has_mojo_irc_server; + my $master_irc_user = $data->{master_irc_user} || $client->account; + #my $image_api = $data->{image_api}; # || 'http://img.vim-cn.com/'; + my $image_api = $data->{image_api} || 'http://img.vim-cn.com/'; + my $is_load_friend = defined $data->{load_friend}?$data->{load_friend}:1; + my @groups = ref($data->{group}) eq "ARRAY"?@{$data->{group}}:(); + my %mode = ref($data->{mode}) eq "HASH"?%{$data->{mode}}:(); + $ircd = Mojo::IRC::Server::Chinese->new(listen=>$data->{listen},log=>$client->log); + $ircd->on(privmsg=>sub{ + my($ircd,$user,$msg) = @_; + if(substr($msg->{params}[0],0,1) eq "#" ){ + my $channel_name = $msg->{params}[0]; + my $content = $msg->{params}[1]; + my $raw_content = $content; + my $channel = $ircd->search_channel(name=>$channel_name); + return if not defined $channel; + my $group = $client->search_group(id=>$channel->id); + return if not defined $group; + if($content=~/^([^\s]+?): /){ + my $at_nick = $1; + $content =~s/^([^\s]+?): /\@$at_nick / if $ircd->search_user(nick=>$at_nick); + $raw_content = $content; + } + if($user->user ne $master_irc_user and !$user->is_localhost){ + $content = $user->nick . ": $content"; + #$content .= " (来自 ".$user->nick.")"; + } + # 向群组里面发送图片命令解析 sendFile /tmp/dog.png + if ($content =~ m/^sendFile /) { + my @commands = split ' ', $content; + $group->send_media($commands[1]); + } + #$group->send($content,sub{ + # $_[1]->from("irc"); + # $_[1]->cb(sub{ + # my($client,$msg,$status)=@_; + # if($status->is_success){ + # if($msg->content ne $raw_content){ + # $msg->content($raw_content); + # } + # } + # else{ + # $user->send($user->ident,"PRIVMSG",$channel_name,$content . "[发送失败]"); + # } + # }); + #}); + } + elsif($user->user eq $master_irc_user or $user->is_localhost){ + my $nick = $msg->{params}[0]; + my $content = $msg->{params}[1]; + my $u = $ircd->search_user(nick=>$nick,virtual=>1); + return if not defined $u; + my $friend = $client->search_friend(id=>$u->id); + if(defined $friend){ + # 检测发送文件的二级命令 sendFile /tmp/dog.png + if ($content =~ m/^sendFile /) { + my @commands = split ' ', $content; + $friend->send_media($commands[1]); + } + + #$friend->send($content,sub{ + # $_[1]->from("irc"); + # $_[1]->cb(sub{ + #my($client,$msg,$status)=@_; + #return if $status->is_success; + #$user->send($user->ident,"PRIVMSG",$nick,$content . "[发送失败]"); + # }); + #}); + } + else{ + $user->send($user->ident,"PRIVMSG",$nick,$content . "[发送失败]"); + $user->send($user->ident,"PRIVMSG",$nick,"你和 [$nick] 并非好友关系"); + } + } + }); + + my $callback = sub{ + my %delete_channel = map {$_->id => $_} grep {$_->name ne "#我的好友"} $ircd->channels; + $ircd->remove_user($_) for grep {$_->is_virtual} $ircd->users; + my $friend_channel = $ircd->new_channel(name=>'#我的好友',mode=>"Pis"); + if($is_load_friend){ + $client->each_friend(sub{ + my($client,$friend) = @_; + my $user = $ircd->search_user(nick=>$friend->displayname,virtual=>0); + if(defined $user){ + $user->once(close=>sub{ + my $virtual_user = $ircd->new_user( + id => $friend->id, + name => $friend->displayname . ":虚拟用户", + nick => $friend->displayname, + user => $friend->id, + virtual => 1, + ); + $virtual_user->join_channel($friend_channel); + }); + } + else{ + my $virtual_user = $ircd->new_user( + id => $friend->id, + name => $friend->displayname . ":虚拟用户", + nick => $friend->displayname, + user => $friend->id, + virtual => 1, + ); + $virtual_user->join_channel($friend_channel); + } + }); + } + $client->each_group(sub{ + my($client,$group) = @_; + return if(@groups and not first {$group->displayname eq $_} @groups); + my $mode = defined $mode{$group->displayname}?$mode{$group->displayname}:"Pi"; + my $channel_name = '#'.$group->displayname;$channel_name=~s/\s|,|&//g; + my $channel = $ircd->search_channel(name=>$channel_name); + if(defined $channel){ + delete $delete_channel{$channel->id}; + $channel->id($group->id); + $channel->remove_user($_) for grep {$_->is_virtual} $channel->users; + } + else{ $ircd->new_channel(id=>$group->id,name=>'#'.$group->displayname,mode=>$mode);} + }); + $ircd->remove_channel($_) for values %delete_channel; + }; + $client->on(ready=>sub{ + $master_irc_user = $data->{master_irc_user} || $client->account; + $callback->(); + $client->on(login=>$callback); + }); + $client->on(receive_message=>sub{ + my($client,$msg) = @_; + if($msg->type eq "friend_message"){ + my $friend = $msg->sender; + my $user = $ircd->search_user(id=>$friend->id,virtual=>1) || $ircd->search_user(nick=>$friend->displayname,virtual=>0); + my $channel = $ircd->search_channel(name=>'#我的好友') || $ircd->new_channel(name=>'#我的好友',mode=>"Pis"); + return if not defined $channel; + if(not defined $user){ + $user = $ircd->new_user( + id =>$friend->id, + name =>$friend->displayname . ":虚拟用户",, + user =>$friend->id, + nick =>$friend->displayname, + virtual => 1, + ); + $user->join_channel($channel); + } + else{ + $user->join_channel($channel) if $user->is_virtual and !$user->is_join_channel($channel); + } + for (grep { $_->user eq $master_irc_user or $_->is_localhost} grep {!$_->is_virtual} $ircd->users){ + for my $line (split /\r?\n/,$msg->content){ + $_->send($user->ident,"PRIVMSG",$_->nick,$line); + $user->send($user->ident,"PRIVMSG",$_->nick,$line); + } + } + } + + elsif($msg->type eq "group_message"){ + my $member = $msg->sender; + #return if not defined $member; + return if @groups and not first {$member->group->displayname eq $_} @groups; + my $user = $ircd->search_user(id=>$member->id,virtual=>1) || $ircd->search_user(nick=>$member->displayname,virtual=>0); + my $channel = $ircd->search_channel(id=>$member->group->id) || + $ircd->new_channel(id=>$member->group->id,name=>'#'.$member->group->displayname,); + return if not defined $channel; + if(not defined $user){ + $user=$ircd->new_user( + id =>$member->id, + name =>$member->displayname . ":虚拟用户", + user =>$member->id, + nick =>$member->displayname, + virtual => 1, + ); + + $user->join_channel($channel); + } + elsif($user->is_virtual){ + $user->join_channel($channel) if not $user->is_join_channel($channel); + } + #else{ + # #$user->join_channel($channel) if not $user->is_join_channel($channel); + #} + + #接收的图片上传到图床 + $client->http_post($image_api,form=>{image=>{content=>$msg->media_data}},sub{ + my($link,$ua,$tx)=@_; + return unless defined $link; + return unless $link=~/https?:\/\//; + $channel->broadcast($user->ident,"PRIVMSG",$channel->name,"图片链接: $link"); + }) if defined $image_api and $msg->format eq 'media'; + + for(grep {!$_->is_virtual} $channel->users){ + my @content = split /\r?\n/,$msg->content; + if($content[0]=~/^\@([^\s]+?) /){ + my $at_nick = $1; + if($ircd->search_user(nick=>$at_nick)){ + $content[0] =~s/^\@([^\s]+?) //; + $_ = "$at_nick: " . $_ for @content; + } + } + for my $line (@content){ + $_->send($user->ident,"PRIVMSG",$channel->name,$line); + } + } + } + + }); + $client->on(send_message=>sub{ + my($client,$msg) = @_; + return if $msg->from eq "irc"; + if($msg->type eq "friend_message"){ + my $friend = $msg->receiver; + my $user = $ircd->search_user(id=>$friend->id,virtual=>1) || $ircd->search_user(nick=>$friend->displayname,virtual=>0); + my $channel = $ircd->search_channel(name=>'#我的好友') || $ircd->new_channel(name=>'#我的好友',mode=>"Pis"); + if(not defined $user){ + $user=$ircd->new_user( + id =>$friend->id, + name =>$friend->displayname . ":虚拟用户", + user =>$friend->id, + nick =>$friend->displayname, + virtual => 1, + ); + $user->join_channel($channel); + } + elsif($user->is_virtual){ + $user->join_channel($channel) if not $user->is_join_channel($channel); + } + #接收的图片上传到图床 + $client->http_post($image_api,form=>{image=>{content=>$msg->media_data}},sub{ + my($link,$ua,$tx)=@_; + return unless defined $link; + return unless $link=~/https?:\/\//; + $channel->broadcast($user->ident,"PRIVMSG",$channel->name,"图片链接: $link"); + }) if defined $image_api and $msg->format eq 'media'; + + for( + grep {$_->user eq $master_irc_user or $_->is_localhost} + grep {!$_->is_virtual} $ircd->users + ) + { + for my $line (split /\r?\n/,$msg->content){ + $_->send($_->ident,"PRIVMSG",$user->nick,$line); + $user->send($_->ident,"PRIVMSG",$user->nick,$line); + } + } + } + elsif($msg->type eq "group_message"){ + return if @groups and not first {$msg->group->displayname eq $_} @groups; + my $channel = $ircd->search_channel(id=>$msg->group->id); + return unless defined $channel; + for my $master_irc_client ( + grep {$_->user eq $master_irc_user or $_->is_localhost} + grep {!$_->is_virtual} $ircd->users + ){ + for(grep {!$_->{virtual}} $channel->users){ + my @content = split /\r?\n/,$msg->content; + if($content[0]=~/^\@([^\s]+?) /){ + my $at_nick = $1; + if($ircd->search_user(nick=>$at_nick)){ + $content[0] =~s/^\@([^\s]+?) //; + map {$_ = "$at_nick: " . $_} @content; + } + } + for my $line (@content){ + $_->send($master_irc_client->ident,"PRIVMSG",$channel->name,$line); + } + } + } + } + }); + + my $property_change_callback = sub{ + my($client,$object,$property,$old,$new)=@_; + if($object->is_friend){ + return if $property ne "nick" and $property ne "markname"; + my $user = $ircd->search_user(id=>$object->id,virtual=>1); + return unless defined $user; + $user->set_nick($object->displayname) if $object->displayname ne $user->nick; + } + elsif($object->is_group_member){ + return if $property ne "nick" and $property ne "card"; + my $user = $ircd->search_user(id=>$object->id,virtual=>1); + return unless defined $user; + $user->set_nick($object->displayname) if $object->displayname ne $user->nick; + } + }; + $client->on("friend_property_change"=>$property_change_callback, + "group_member_property_change"=>$property_change_callback + ); + + $ircd->ready(); +} +1; diff --git a/weixin.pl b/weixin.pl new file mode 100644 index 0000000..3990d6e --- /dev/null +++ b/weixin.pl @@ -0,0 +1,9 @@ +#!/usr/bin/env perl +use Mojo::Weixin; +my $client = Mojo::Weixin->new(); +$client->load("IRCShell",data=>{ + listen=>[ {host=>"127.0.0.1",port=>6667},], #可选,IRC服务器监听的地址+端口,默认0.0.0.0:6667 + load_friend => 1, #默认是0 是否初始为每个好友生成irc虚拟帐号并加入频道 #我的好友 +}); +$client->load("ShowMsg"); +$client->run(); diff --git a/weixinv1.pl b/weixinv1.pl new file mode 100644 index 0000000..6647bf0 --- /dev/null +++ b/weixinv1.pl @@ -0,0 +1,46 @@ +#!/usr/bin/env perl +use Mojo::Weixin; + +my $client = Mojo::Weixin->new(); +$client->load("IRCShell",data=>{ + listen=>[ {host=>"127.0.0.1",port=>6667},], #可选,IRC服务器监听的地址+端口,默认0.0.0.0:6667 + load_friend => 1, #默认是0 是否初始为每个好友生成irc虚拟帐号并加入频道 #我的好友 +}); +# $client->load("ShowMsg"); + +#ready事件触发时 表示客户端一切准备就绪:已经成功登录、已经加载完个人/好友/群信息等 +#你的代码建议尽量写在 ready 事件中 +$client->on(ready=>sub{ + my $client = shift; + + #设置接收消息事件的回调函数,在回调函数中对消息以相同内容进行回复 + $client->on(receive_message=>sub{ + my ($client,$msg)=@_; + # $msg->reply($msg->content); #已以相同内容回复接收到的消息 + #你也可以使用$msg->dump() 来打印消息结构 + # print $msg->dump() + if($msg->type eq "friend_message"){#接收到好友消息 + my $friend = $msg->sender; #获取到该好友对象 + my $rcvMsg = $friend->displayname . ":" . $msg->content; + system("printf '\7'"); + system("notify-send " . $rcvMsg); + print $rcvMsg; + # print "我的好友:" . $friend->displayname . "给我发了一个消息,消息的内容是:" . $msg->content; + } + elsif($msg->type eq "group_message"){#接收到群消息 + my $group = $msg->group; #获取到消息对应的群组对象 + my $sender = $msg->sender; #获取到发送该消息的群成员对象 + + print $sender->displayname . "在群:", $group->displayname . "中发了一条消息,消息的内容是:" . $msg->content; + } + elsif($msg->type eq "group_notice"){ + my $group = $msg->group; #获取到消息对应的群组对象 + print "在群:", $group->displayname . "中收到了一条群提示消息,消息的内容是:" . $msg->content; + } + }); + + #你的其他代码写在此处 + +}); + +$client->run();