Commit c77778a2 by HanSon

优化消息

1 parent 540f3f4e
...@@ -296,20 +296,44 @@ $robot->server->setCustomHandler(function(){ ...@@ -296,20 +296,44 @@ $robot->server->setCustomHandler(function(){
# 特别感谢 # 特别感谢
[liuwons/wxBot](https://github.com/liuwons/wxBot) 参考了整个微信的登录流程与消息处理 [lbbniu/WebWechat](https://github.com/lbbniu/WebWechat)
[littlecodersh/ItChat](https://github.com/littlecodersh/ItChat)
感谢以上两位作者曾对本人耐心解答
## 参考项目
[overtrue/wechat](https://github.com/overtrue/wechat) 参考了部分代码的书写格式与设计思路 [liuwons/wxBot](https://github.com/liuwons/wxBot) 参考了整个微信的登录流程与消息处理
# to do list # to do list
- [ ] 命令行操作信息发送 - [ ] 消息处理
- [x] 文字
- [x] 图片
- [x] 语音
- [x] 位置
- [x] 视频
- [x] 撤回
- [x] 表情
- [x] 红包
- [x] 转账
- [x] 名片
- [ ] 好友验证
- [ ] 分享
- [ ] 小程序
- [x] 消息存储
- [x] 语音
- [x] 图片
- [x] 视频
- [x] 表情
- [ ] 抽象消息 - [x] 消息发送
- [ ] 图片 - [x] 发送文字
- [ ] 视频 - [x] 发送图片
- [ ] 文字 - [x] 发送表情
- [ ] 语音 - [x] 发送视频
- [ ] 表情
- [ ] 群操作 - [ ] 群操作
- [ ] 创建群 - [ ] 创建群
...@@ -317,40 +341,15 @@ $robot->server->setCustomHandler(function(){ ...@@ -317,40 +341,15 @@ $robot->server->setCustomHandler(function(){
- [ ] 邀请好友加入群 - [ ] 邀请好友加入群
- [ ] 修改群名称 - [ ] 修改群名称
- [ ] 聊天窗口操作
- [ ] 置顶聊天会话
- [ ] 取消聊天会话指定
- [ ] 好友操作 - [ ] 好友操作
- [ ] 给好友添加备注 - [ ] 给好友添加备注
- [ ] 通过好友验证 - [ ] 通过好友验证
- [x] 增加消息集合存储 - [ ] 聊天窗口操作
- [ ] 置顶聊天会话
- [x] 消息发送 - [ ] 取消聊天会话指定
- [x] 发送文字
- [x] 发送图片
- [x] 发送表情
- [x] 发送视频
- [x] 消息存储
- [x] 语音
- [x] 图片
- [x] 视频
- [x] 表情
- [ ] 消息处理 - [ ] 命令行操作信息发送
- [x] 文字
- [x] 图片
- [x] 语音
- [x] 位置
- [x] 撤回
- [x] 表情
- [ ] 好友验证
- [ ] 名片
- [ ] 分享
- [ ] 视频
- [ ] 小程序
# 已知bug # 已知bug
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
require_once __DIR__ . './../vendor/autoload.php'; require_once __DIR__ . './../vendor/autoload.php';
use Hanson\Robot\Foundation\Robot; use Hanson\Robot\Foundation\Robot;
use Hanson\Robot\Message\Message; use Hanson\Robot\Message\Text;
$robot = new Robot([ $robot = new Robot([
'tmp' => __DIR__ . '/./../tmp/', 'tmp' => __DIR__ . '/./../tmp/',
...@@ -18,14 +18,13 @@ $robot = new Robot([ ...@@ -18,14 +18,13 @@ $robot = new Robot([
$flag = false; $flag = false;
$robot->server->setCustomerHandler(function() use (&$flag){ $robot->server->setCustomerHandler(function() use (&$flag){
/** @var $message Message */
if(!$flag){ if(!$flag){
Message::send('custom', contact()->getUsernameById('L907159127')); Text::send('custom', contact()->getUsernameById('L907159127'));
$flag = true; $flag = true;
} }
Message::send('测试' . \Carbon\Carbon::now()->toDateTimeString(), contact()->getUsernameById('L907159127')); Text::send('测试' . \Carbon\Carbon::now()->toDateTimeString(), contact()->getUsernameById('L907159127'));
}); });
......
...@@ -20,7 +20,7 @@ $robot->server->setMessageHandler(function($message){ ...@@ -20,7 +20,7 @@ $robot->server->setMessageHandler(function($message){
if($message->type === 'Text'){ if($message->type === 'Text'){
/** @var $message Message */ /** @var $message Message */
$contact = contact()->getUsernameById('hanson1994'); $contact = contact()->getUsernameById('hanson1994');
Message::send($message->content, $contact); Text::send($message->content, $contact);
} }
}); });
$robot->server->setCustomerHandler(function(){ $robot->server->setCustomerHandler(function(){
......
...@@ -18,7 +18,7 @@ $robot = new Robot([ ...@@ -18,7 +18,7 @@ $robot = new Robot([
$robot->server->setMessageHandler(function($message){ $robot->server->setMessageHandler(function($message){
/** @var $message Message */ /** @var $message Message */
if($message->type === 'Text'){ if($message->type === 'Text'){
Message::send($message->fromType, $message->username); Text::send($message->fromType, $message->username);
} }
}); });
$robot->server->run(); $robot->server->run();
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
require_once __DIR__ . './../vendor/autoload.php'; require_once __DIR__ . './../vendor/autoload.php';
use Hanson\Robot\Foundation\Robot; use Hanson\Robot\Foundation\Robot;
use Hanson\Robot\Message\Message; use Hanson\Robot\Message\Text;
$robot = new Robot([ $robot = new Robot([
'tmp' => __DIR__ . '/./../tmp/', 'tmp' => __DIR__ . '/./../tmp/',
...@@ -17,10 +17,9 @@ $robot = new Robot([ ...@@ -17,10 +17,9 @@ $robot = new Robot([
]); ]);
$robot->server->setCustomerHandler(function(){ $robot->server->setCustomerHandler(function(){
/** @var $message Message */
$group = group()->getGroupsByNickname('stackoverflow', true)->first(); $group = group()->getGroupsByNickname('stackoverflow', true)->first();
Message::send('测试' . \Carbon\Carbon::now()->toDateTimeString(), $group['UserName']); Text::send($group['UserName'], '测试' . \Carbon\Carbon::now()->toDateTimeString());
}); });
......
...@@ -22,7 +22,7 @@ $robot->server->setCustomerHandler(function(){ ...@@ -22,7 +22,7 @@ $robot->server->setCustomerHandler(function(){
$groups = group()->getGroupsByNickname('stackoverflow', true); $groups = group()->getGroupsByNickname('stackoverflow', true);
foreach ($groups as $group) { foreach ($groups as $group) {
Message::send('测试' . \Carbon\Carbon::now()->toDateTimeString(), $group['UserName']); Text::send('测试' . \Carbon\Carbon::now()->toDateTimeString(), $group['UserName']);
} }
}); });
......
...@@ -25,7 +25,7 @@ $robot->server->setMessageHandler(function($message){ ...@@ -25,7 +25,7 @@ $robot->server->setMessageHandler(function($message){
} }
if($message->type === 'Recall' && $message->rawMsg['FromUserName'] !== myself()->username){ if($message->type === 'Recall' && $message->rawMsg['FromUserName'] !== myself()->username){
Console::log($message->content); Console::log($message->content);
Message::send($message->content, $message->username); Text::send($message->content, $message->username);
Image::send($message->username, realpath(__DIR__ . '/./../tmp/jpg/1547651860337387181.jpg')); Image::send($message->username, realpath(__DIR__ . '/./../tmp/jpg/1547651860337387181.jpg'));
} }
......
...@@ -9,13 +9,20 @@ ...@@ -9,13 +9,20 @@
require_once __DIR__ . './../vendor/autoload.php'; require_once __DIR__ . './../vendor/autoload.php';
use Hanson\Robot\Foundation\Robot; use Hanson\Robot\Foundation\Robot;
use Hanson\Robot\Message\Message; use Hanson\Robot\Message\Entity\Message;
use Hanson\Robot\Message\Image; use Hanson\Robot\Message\Entity\Image;
use Hanson\Robot\Message\Text; use Hanson\Robot\Message\Entity\Text;
use Hanson\Robot\Message\Emoticon; use Hanson\Robot\Message\Entity\Emoticon;
use Hanson\Robot\Message\Location; use Hanson\Robot\Message\Entity\Location;
use Hanson\Robot\Message\Video; use Hanson\Robot\Message\Entity\Video;
use Hanson\Robot\Message\MessageInterface; use Hanson\Robot\Message\Entity\Voice;
use Hanson\Robot\Message\Entity\Recall;
use Hanson\Robot\Message\Entity\RedPacket;
use Hanson\Robot\Message\Entity\Transfer;
use Hanson\Robot\Message\Entity\Recommend;
use Hanson\Robot\Message\Entity\Share;
use Hanson\Robot\Message\Entity\Touch;
use Hanson\Robot\Message\Entity\RequestFriend;
$path = __DIR__ . '/./../tmp/'; $path = __DIR__ . '/./../tmp/';
$robot = new Robot([ $robot = new Robot([
...@@ -23,36 +30,127 @@ $robot = new Robot([ ...@@ -23,36 +30,127 @@ $robot = new Robot([
'debug' => true 'debug' => true
]); ]);
$robot->server->setMessageHandler(function($message) use ($path){ $robot->server->setMessageHandler(function ($message) use ($path) {
/** @var $message Message */ /** @var $message Message */
// 位置信息 返回位置文字 // 位置信息 返回位置文字
if($message instanceof Location){ if ($message instanceof Location) {
return $message; return $message;
} }
// 发送撤回消息 (排除自己) // 文字信息
// if($message->type === 'Recall' && $message->rawMsg['FromUserName'] !== myself()->username && $message->username === group()->getGroupsByNickname('stackoverflow', true)->first()['UserName']){ if ($message instanceof Text) {
// $msg = message()->get($message->msgId); // 联系人自动回复
// if($msg){ if ($message->fromType === 'Contact') {
// $nickname = $msg['sender'] ? $msg['sender']['NickName'] : account()->getAccount($msg['username'])['NickName'];
// if($msg['type'] === 'Image'){ return http()->post('http://www.tuling123.com/openapi/api', [
// Text::send($message->username, "{$nickname} 撤回了一张照片"); 'key' => '1dce02aef026258eff69635a06b0ab7d',
// Image::send($message->username, realpath($path . "jpg/{$message->msgId}.jpg")); 'info' => $message->content
// }elseif($msg['type'] === 'Emoticon'){ ], true)['text'];
// Text::send($message->username, "{$nickname} 撤回了一个表情"); // 群组@我回复
// Emoticon::send($message->username, realpath($path . "gif/{$message->msgId}.gif")); } elseif ($message->fromType === 'Group' && $message->isAt) {
// }elseif($msg['type'] === 'Video' || $msg['type'] === 'VideoCall'){ return http()->post('http://www.tuling123.com/openapi/api', [
// Text::send($message->username, "{$nickname} 撤回了一个视频"); 'key' => '1dce02aef026258eff69635a06b0ab7d',
// Video::send($message->username, realpath($path . "mp4/{$message->msgId}.mp4")); 'info' => $message->content
// }elseif($msg['type'] === 'Voice'){ ], true)['text'];
// Text::send($message->username, "{$nickname} 撤回了一条语音"); }
// }else{ }
// Text::send($message->username, "{$nickname} 撤回了一条信息 \"{$msg['content']}\"");
// 图片信息 返回接收到的图片
// if ($message instanceof Image) {
// return $message;
// } // }
// 视频信息 返回接收到的视频
// if ($message instanceof Video) {
// return $message;
// }
// 表情信息 返回接收到的表情
// if ($message instanceof Emoticon) {
// return $message;
// } // }
// 语音消息
// if($message instanceof Voice){
// /** @var $message Voice */
// return '收到一条语音并下载在' . $message->getPath($message::$folder) . "/{$message->msg['MsgId']}.mp3";
// } // }
// 撤回信息
if ($message instanceof Recall && $message->msg['FromUserName'] !== myself()->username) {
/** @var $message Recall */
if($message->origin instanceof Image){
Text::send($message->msg['FromUserName'], "{$message->nickname} 撤回了一张照片");
Image::sendByMsgId($message->msg['FromUserName'], $message->origin->msg['MsgId']);
}elseif($message->origin instanceof Emoticon){
Text::send($message->msg['FromUserName'], "{$message->nickname} 撤回了一个表情");
Emoticon::sendByMsgId($message->msg['FromUserName'], $message->origin->msg['MsgId']);
}elseif($message->origin instanceof Video){
Text::send($message->msg['FromUserName'], "{$message->nickname} 撤回了一个视频");
Video::sendByMsgId($message->msg['FromUserName'], $message->origin->msg['MsgId']);
}elseif($message->origin instanceof Voice){
Text::send($message->msg['FromUserName'], "{$message->nickname} 撤回了一条语音");
}else{
Text::send($message->msg['FromUserName'], "{$message->nickname} 撤回了一条信息 \"{$message->origin->msg['Content']}\"");
}
// return $message;
}
// 红包信息
if($message instanceof RedPacket){
// do something to notify if you want ...
return $message->content . ' 来自 ' .$message->from['NickName'];
}
// 转账信息
if($message instanceof Transfer){
/** @var $message Transfer */
return $message->content . ' 收到金额 ' . $message->fee;
}
// 推荐名片信息
if($message instanceof Recommend){
/** @var $message Recommend */
if($message->isOfficial){
return $message->from['NickName'] . ' 向你推荐了公众号 ' . $message->province . $message->city .
" {$message->info['NickName']} 公众号信息: {$message->description}";
}else{
return $message->from['NickName'] . ' 向你推荐了 ' . $message->province . $message->city .
" {$message->info['NickName']} 头像链接: {$message->bigAvatar}";
}
}
// 请求添加信息
if($message instanceof RequestFriend){
/** @var $message RequestFriend */
$groupUsername = group()->getGroupsByNickname('芬芬', true)->first()['UserName'];
Text::send($groupUsername, "{$message->province}{$message->city}{$message->info['NickName']} 请求添加好友 \"{$message->info['Content']}\"");
if($message->info['Content'] === '上山打老虎'){
Text::send($groupUsername, '暗号正确');
$message->verifyUser($message::VIA);
}else{
Text::send($groupUsername, '暗号错误');
}
}
// 分享信息
if($message instanceof Share){
/** @var $message Share */
$reply = "收到分享\n标题:{$message->title}\n描述:{$message->description}\n链接:{$message->url}";
if($message->app){
$reply .= "\n来源APP:{$message->app}";
}
return $reply;
}
// 手机点击聊天事件
if($message instanceof Touch){
Text::send($message->to['UserName'], "我点击了此群");
}
}); });
$robot->server->run(); $robot->server->run();
...@@ -55,7 +55,7 @@ class Contact extends Collection ...@@ -55,7 +55,7 @@ class Contact extends Collection
public function getContactById($id) public function getContactById($id)
{ {
$contact = $this->filter(function($item, $key) use ($id){ $contact = $this->filter(function($item, $key) use ($id){
if($item->Alias === $id){ if($item['Alias'] === $id){
return true; return true;
} }
})->first(); })->first();
...@@ -72,7 +72,7 @@ class Contact extends Collection ...@@ -72,7 +72,7 @@ class Contact extends Collection
public function getUsernameById($id) public function getUsernameById($id)
{ {
$contact = $this->search(function($item, $key) use ($id){ $contact = $this->search(function($item, $key) use ($id){
if($item->Alias === $id){ if($item['Alias'] === $id){
return true; return true;
} }
}); });
......
...@@ -11,11 +11,9 @@ namespace Hanson\Robot\Collections; ...@@ -11,11 +11,9 @@ namespace Hanson\Robot\Collections;
use Hanson\Robot\Core\Server; use Hanson\Robot\Core\Server;
use Hanson\Robot\Support\Console; use Hanson\Robot\Support\Console;
use Hanson\Robot\Support\ObjectAble;
class ContactFactory class ContactFactory
{ {
use ObjectAble;
const SPECIAL_USERS = ['newsapp', 'fmessage', 'filehelper', 'weibo', 'qqmail', const SPECIAL_USERS = ['newsapp', 'fmessage', 'filehelper', 'weibo', 'qqmail',
'fmessage', 'tmessage', 'qmessage', 'qqsync', 'floatbottle', 'fmessage', 'tmessage', 'qmessage', 'qqsync', 'floatbottle',
...@@ -33,7 +31,7 @@ class ContactFactory ...@@ -33,7 +31,7 @@ class ContactFactory
public function getContacts() public function getContacts()
{ {
$url = sprintf(Server::BASE_URI . '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s', server()->passTicket, server()->skey, time()); $url = sprintf(server()->baseUri . '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s', server()->passTicket, server()->skey, time());
$content = http()->json($url, [ $content = http()->json($url, [
'BaseRequest' => server()->baseRequest 'BaseRequest' => server()->baseRequest
...@@ -50,14 +48,14 @@ class ContactFactory ...@@ -50,14 +48,14 @@ class ContactFactory
protected function makeContactList($memberList) protected function makeContactList($memberList)
{ {
foreach ($memberList as $contact) { foreach ($memberList as $contact) {
if(($contact['VerifyFlag'] & 8) != 0){ #公众号 if(official()->isOfficial($contact['VerifyFlag'])){ #公众号
OfficialAccount::getInstance()->put($contact['UserName'], $this->toObject($contact)); Official::getInstance()->put($contact['UserName'], $contact);
}elseif (in_array($contact['UserName'], static::SPECIAL_USERS)){ # 特殊账户 }elseif (in_array($contact['UserName'], static::SPECIAL_USERS)){ # 特殊账户
SpecialAccount::getInstance()->put($contact['UserName'], $this->toObject($contact)); SpecialAccount::getInstance()->put($contact['UserName'], $contact);
}elseif (strstr($contact['UserName'], '@@') !== false){ # 群聊 }elseif (strstr($contact['UserName'], '@@') !== false){ # 群聊
group()->put($contact['UserName'], $this->toObject($contact)); group()->put($contact['UserName'], $contact);
}else{ }else{
contact()->put($contact['UserName'], $this->toObject($contact)); contact()->put($contact['UserName'], $contact);
} }
} }
...@@ -66,7 +64,7 @@ class ContactFactory ...@@ -66,7 +64,7 @@ class ContactFactory
file_put_contents(server()->config['tmp'] . 'contact.json', json_encode(contact()->all())); file_put_contents(server()->config['tmp'] . 'contact.json', json_encode(contact()->all()));
file_put_contents(server()->config['tmp'] . 'member.json', json_encode(member()->all())); file_put_contents(server()->config['tmp'] . 'member.json', json_encode(member()->all()));
file_put_contents(server()->config['tmp'] . 'group.json', json_encode(group()->all())); file_put_contents(server()->config['tmp'] . 'group.json', json_encode(group()->all()));
file_put_contents(server()->config['tmp'] . 'OfficialAccount.json', json_encode(OfficialAccount::getInstance()->all())); file_put_contents(server()->config['tmp'] . 'OfficialAccount.json', json_encode(Official::getInstance()->all()));
file_put_contents(server()->config['tmp'] . 'SpecialAccount.json', json_encode(SpecialAccount::getInstance()->all())); file_put_contents(server()->config['tmp'] . 'SpecialAccount.json', json_encode(SpecialAccount::getInstance()->all()));
} }
} }
...@@ -76,7 +74,7 @@ class ContactFactory ...@@ -76,7 +74,7 @@ class ContactFactory
*/ */
public function getBatchGroupMembers() public function getBatchGroupMembers()
{ {
$url = sprintf(Server::BASE_URI . '/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s', time(), server()->passTicket); $url = sprintf(server()->baseUri . '/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s', time(), server()->passTicket);
$list = []; $list = [];
group()->each(function($item, $key) use (&$list){ group()->each(function($item, $key) use (&$list){
...@@ -103,9 +101,9 @@ class ContactFactory ...@@ -103,9 +101,9 @@ class ContactFactory
$groupAccount = group()->get($group['UserName']); $groupAccount = group()->get($group['UserName']);
$groupAccount['MemberList'] = $group['MemberList']; $groupAccount['MemberList'] = $group['MemberList'];
$groupAccount['ChatRoomId'] = $group['EncryChatRoomId']; $groupAccount['ChatRoomId'] = $group['EncryChatRoomId'];
group()->put($group['UserName'], $this->toObject($groupAccount)); group()->put($group['UserName'], $groupAccount);
foreach ($group['MemberList'] as $member) { foreach ($group['MemberList'] as $member) {
member()->put($member['UserName'], $this->toObject($member)); member()->put($member['UserName'], $member);
} }
} }
......
...@@ -49,20 +49,16 @@ class Group extends Collection ...@@ -49,20 +49,16 @@ class Group extends Collection
* @param bool $onlyUsername * @param bool $onlyUsername
* @return static * @return static
*/ */
public function getGroupsByNickname($name, $blur = false, $onlyUsername = false) public function getGroupsByNickname($name, $blur = false)
{ {
$groups = $this->filter(function($value, $key) use ($name, $blur){ $groups = $this->filter(function($value, $key) use ($name, $blur){
if(!$blur){ if(!$blur){
return $value->NickName === $name; return $value['NickName'] === $name;
}else{ }else{
return str_contains($value->NickName, $name); return str_contains($value['NickName'], $name);
} }
}); });
if($onlyUsername){
$groups = $groups->only(['UserName']);
}
return $groups; return $groups;
} }
......
...@@ -11,31 +11,31 @@ namespace Hanson\Robot\Collections; ...@@ -11,31 +11,31 @@ namespace Hanson\Robot\Collections;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
class OfficialAccount extends Collection class Official extends Collection
{ {
/** /**
* @var OfficialAccount * @var Official
*/ */
static $instance = null; static $instance = null;
/** /**
* create a single instance * create a single instance
* *
* @return OfficialAccount * @return Official
*/ */
public static function getInstance() public static function getInstance()
{ {
if(static::$instance === null){ if(static::$instance === null){
static::$instance = new OfficialAccount(); static::$instance = new Official();
} }
return static::$instance; return static::$instance;
} }
public function isPublic($id) public function isOfficial($verifyFlag)
{ {
return static::$instance->get($id, false); return ($verifyFlag & 8) != 0;
} }
} }
\ No newline at end of file \ No newline at end of file
...@@ -9,9 +9,20 @@ ...@@ -9,9 +9,20 @@
namespace Hanson\Robot\Core; namespace Hanson\Robot\Core;
use Hanson\Robot\Message\Location; use Hanson\Robot\Message\Entity\Emoticon;
use Hanson\Robot\Message\Entity\Image;
use Hanson\Robot\Message\Entity\Location;
use Hanson\Robot\Message\Entity\Recall;
use Hanson\Robot\Message\Entity\Recommend;
use Hanson\Robot\Message\Entity\RedPacket;
use Hanson\Robot\Message\Entity\RequestFriend;
use Hanson\Robot\Message\Entity\Share;
use Hanson\Robot\Message\Entity\Text;
use Hanson\Robot\Message\Entity\Touch;
use Hanson\Robot\Message\Entity\Transfer;
use Hanson\Robot\Message\Entity\Video;
use Hanson\Robot\Message\Entity\Voice;
use Hanson\Robot\Support\Console; use Hanson\Robot\Support\Console;
use Hanson\Robot\Support\FileManager;
class MessageFactory class MessageFactory
{ {
...@@ -32,82 +43,51 @@ class MessageFactory ...@@ -32,82 +43,51 @@ class MessageFactory
*/ */
private function handleMessageByType() private function handleMessageByType()
{ {
Console::log($this->msg['MsgType']);
switch($this->msg['MsgType']){ switch($this->msg['MsgType']){
case 1: //文本消息 case 1: //文本消息
if(Location::isLocation($this->msg)){ if(Location::isLocation($this->msg)){
return new Location($this->msg); return new Location($this->msg);
}else{ }else{
$this->type = 'Text'; return new Text($this->msg);
$this->content = $this->msg['Content'];
} }
break;
case 3: // 图片消息 case 3: // 图片消息
$this->type = 'Image'; return new Image($this->msg);
$this->content = Server::BASE_URI . sprintf('/webwxgetmsgimg?MsgID=%s&skey=%s', $this->msg['MsgId'], server()->skey);
$content = http()->get($this->content);
FileManager::download($this->msg['MsgId'].'.jpg', $content, 'jpg');
break;
case 34: // 语音消息 case 34: // 语音消息
$this->type = 'Voice'; return new Voice($this->msg);
$this->content = Server::BASE_URI . sprintf('/webwxgetvoice?msgid=%s&skey=%s', $this->msg['MsgId'], server()->skey); case 43: // 视频
$content = http()->get($this->content); return new Video($this->msg);
FileManager::download($this->msg['MsgId'].'.mp3', $content, 'mp3');
break;
case 37: // 好友验证
$this->type = 'AddUser';
break;
case 42: //共享名片
$this->type = 'Recommend';
$this->content = (object)$this->msg['RecommendInfo'];
break;
case 43:
$this->type = 'VideoCall';
Console::log('video');
$url = Server::BASE_URI . sprintf('/webwxgetvideo?msgid=%s&skey=%s', $this->msg['MsgId'], server()->skey);
$content = http()->request($url, 'get', [
'headers' => [
'Range' => 'bytes=0-'
]
]);
FileManager::download($this->msg['MsgId'].'.mp4', $content, 'mp4');
break;
case 47: // 动画表情 case 47: // 动画表情
$this->type = 'Emoticon'; return new Emoticon($this->msg);
$this->content = Server::BASE_URI . sprintf('/webwxgetmsgimg?MsgID=%s&skey=%s', $this->msg['MsgId'], server()->skey); case 10002:
$content = http()->get($this->content); return new Recall($this->msg);
FileManager::download($this->msg['MsgId'].'.gif', $content, 'gif'); case 10000:
if($this->msg['Status'] == 4){
return new RedPacket($this->msg);
}else{
}
break; break;
case 49: case 49:
$this->type = 'Share'; if($this->msg['Status'] == 3 && $this->msg['FileName'] === '微信转账'){
break; return new Transfer($this->msg);
}else{
return new Share($this->msg);
}
case 37: // 好友验证
return new RequestFriend($this->msg);
case 42: //共享名片
return new Recommend($this->msg);
case 62: case 62:
$this->type = 'Video'; $this->type = 'Video';
Console::log('video');
$url = Server::BASE_URI . sprintf('/webwxgetvideo?msgid=%s&skey=%s', $this->msg['MsgId'], server()->skey);
$content = http()->request($url, 'get', [
'headers' => [
'Range' => 'bytes=0-'
]
]);
FileManager::download($this->msg['MsgId'].'.mp4', $content, 'mp4');
break; break;
case 51: case 51:
$this->type = 'Init'; if($this->msg['ToUserName'] === $this->msg['StatusNotifyUserName']){
return new Touch($this->msg);
}
break; break;
case 53: case 53:
$this->type = 'VideoCall'; $this->type = 'VideoCall';
break; break;
case 10000:
if($this->msg['Status'] == 4){
$this->type = 'RedPacket'; // 红包
}else{
$this->type = 'Unknown';
}
break;
case 10002:
$this->type = 'Recall'; // 撤回
$this->msgId = $msgId = $this->parseMsgId($this->msg['Content']);
break;
default: default:
$this->type = 'Unknown'; $this->type = 'Unknown';
break; break;
......
...@@ -10,7 +10,11 @@ namespace Hanson\Robot\Core; ...@@ -10,7 +10,11 @@ namespace Hanson\Robot\Core;
use Closure; use Closure;
use Hanson\Robot\Collections\Account; use Hanson\Robot\Collections\Account;
use Hanson\Robot\Message\Message; use Hanson\Robot\Message\Entity\Emoticon;
use Hanson\Robot\Message\Entity\Image;
use Hanson\Robot\Message\Entity\Message;
use Hanson\Robot\Message\Entity\Text;
use Hanson\Robot\Message\Entity\Video;
use Hanson\Robot\Support\Console; use Hanson\Robot\Support\Console;
class MessageHandler class MessageHandler
...@@ -121,14 +125,35 @@ class MessageHandler ...@@ -121,14 +125,35 @@ class MessageHandler
foreach ($message['AddMsgList'] as $msg) { foreach ($message['AddMsgList'] as $msg) {
// $content = (new Message)->make($selector, $msg); // $content = (new Message)->make($selector, $msg);
$content = $this->messageFactory->make($selector, $msg); $content = $this->messageFactory->make($selector, $msg);
if($content){
$this->addToMessageCollection($content);
}
if($this->handler instanceof Closure){ if($this->handler instanceof Closure){
$reply = call_user_func_array($this->handler, [$content]); $reply = call_user_func_array($this->handler, [$content]);
if($reply){ if($reply){
Message::send($reply, $content->from->UserName); if($reply instanceof Image){
Image::sendByMsgId($content->from['UserName'], $reply->msg['MsgId']);
}elseif($reply instanceof Video){
Video::sendByMsgId($content->from['UserName'], $reply->msg['MsgId']);
}elseif($reply instanceof Emoticon){
Emoticon::sendByMsgId($content->from['UserName'], $reply->msg['MsgId']);
}else{
Text::send($content->from['UserName'], $reply);
}
} }
} }
} }
} }
} }
/**
* @param $message Message
*/
private function addToMessageCollection($message)
{
message()->put($message->msg['MsgId'], $message);
file_put_contents(server()->config['tmp'].'/message.json', json_encode(message()->all()));
}
} }
\ No newline at end of file \ No newline at end of file
...@@ -22,8 +22,6 @@ use Symfony\Component\DomCrawler\Crawler; ...@@ -22,8 +22,6 @@ use Symfony\Component\DomCrawler\Crawler;
class Server class Server
{ {
use ObjectAble;
static $instance; static $instance;
protected $uuid; protected $uuid;
...@@ -52,9 +50,13 @@ class Server ...@@ -52,9 +50,13 @@ class Server
protected $debug = false; protected $debug = false;
const BASE_URI = 'https://wx.qq.com/cgi-bin/mmwebwx-bin'; public $baseUri = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin';
public $fileUri;
const BASE_HOST = 'wx.qq.com'; public $pushUri;
const BASE_HOST = 'wx2.qq.com';
public function __construct($config = []) public function __construct($config = [])
{ {
...@@ -173,6 +175,26 @@ class Server ...@@ -173,6 +175,26 @@ class Server
preg_match('/window.redirect_uri="(\S+?)";/', $content, $matches); preg_match('/window.redirect_uri="(\S+?)";/', $content, $matches);
$this->redirectUri = $matches[1] . '&fun=new'; $this->redirectUri = $matches[1] . '&fun=new';
Console::log('登录URL:'.$this->redirectUri); Console::log('登录URL:'.$this->redirectUri);
$domainList = [
'wx2.qq.com' => ['file.wx2.qq.com', 'webpush.wx2.qq.com'],
'wx8.qq.com' => ['file.wx8.qq.com', 'webpush.wx8.qq.com'],
'qq.com' => ['file.wx.qq.com', 'webpush.wx.qq.com'],
'web2.wechat.com' => ['file.web2.wechat.com', 'webpushweb2.wechat.com'],
'wechat.com' => ['file.web.wechat.com', 'webpushweb.web.wechat.com'],
];
$url = 'https://%s/cgi-bin/mmwebwx-bin';
foreach ($domainList as $domain => $list) {
if(str_contains($this->redirectUri, $domain)){
$this->fileUri = sprintf($url, $list[0]);
$this->pushUri = sprintf($url, $list[1]);
$this->baseUri = sprintf($url, $domain);
break;
}else{
// $this->fileUri = $this->pushUri = $
throw new \Exception('I can\'t believe it will be here');
}
}
Console::log('url is:'. $this->baseUri);
return; return;
case '408': case '408':
Console::log('[ERROR] login timeout. please try 1 second later.'); Console::log('[ERROR] login timeout. please try 1 second later.');
...@@ -226,7 +248,7 @@ class Server ...@@ -226,7 +248,7 @@ class Server
protected function init($first = true) protected function init($first = true)
{ {
$url = sprintf(self::BASE_URI . '/webwxinit?r=%d', time()); $url = sprintf($this->baseUri . '/webwxinit?r=%d', time());
$content = http()->json($url, [ $content = http()->json($url, [
'BaseRequest' => $this->baseRequest 'BaseRequest' => $this->baseRequest
...@@ -242,7 +264,7 @@ class Server ...@@ -242,7 +264,7 @@ class Server
if($result['BaseResponse']['Ret'] != 0){ if($result['BaseResponse']['Ret'] != 0){
// print_r($this->baseRequest); // print_r($this->baseRequest);
// Console::log('init URL:'. $url); Console::log('init URL:'. $url);
throw new Exception('[ERROR] init fail!'); throw new Exception('[ERROR] init fail!');
} }
} }
...@@ -252,7 +274,7 @@ class Server ...@@ -252,7 +274,7 @@ class Server
if($contactList){ if($contactList){
foreach ($contactList as $contact) { foreach ($contactList as $contact) {
if(Group::isGroup($contact['UserName'])){ if(Group::isGroup($contact['UserName'])){
group()->put($contact['UserName'], $this->toObject($contact)); group()->put($contact['UserName'], $contact);
} }
} }
} }
...@@ -268,7 +290,7 @@ class Server ...@@ -268,7 +290,7 @@ class Server
*/ */
protected function statusNotify() protected function statusNotify()
{ {
$url = sprintf(self::BASE_URI . '/webwxstatusnotify?lang=zh_CN&pass_ticket=%s', $this->passTicket); $url = sprintf($this->baseUri . '/webwxstatusnotify?lang=zh_CN&pass_ticket=%s', $this->passTicket);
http()->json($url, [ http()->json($url, [
'BaseRequest' => $this->baseRequest, 'BaseRequest' => $this->baseRequest,
......
...@@ -44,7 +44,7 @@ class Sync ...@@ -44,7 +44,7 @@ class Sync
public function sync() public function sync()
{ {
$url = sprintf(Server::BASE_URI . '/webwxsync?sid=%s&skey=%s&lang=en_US&pass_ticket=%s', server()->sid, server()->skey, server()->passTicket); $url = sprintf(server()->baseUri . '/webwxsync?sid=%s&skey=%s&lang=en_US&pass_ticket=%s', server()->sid, server()->skey, server()->passTicket);
try{ try{
$result = http()->json($url, [ $result = http()->json($url, [
...@@ -74,9 +74,11 @@ class Sync ...@@ -74,9 +74,11 @@ class Sync
$syncKey = []; $syncKey = [];
if(is_array(server()->syncKey['List'])){
foreach (server()->syncKey['List'] as $item) { foreach (server()->syncKey['List'] as $item) {
$syncKey[] = $item['Key'] . '_' . $item['Val']; $syncKey[] = $item['Key'] . '_' . $item['Val'];
} }
}
server()->syncKeyStr = implode('|', $syncKey); server()->syncKeyStr = implode('|', $syncKey);
} }
......
...@@ -6,16 +6,29 @@ ...@@ -6,16 +6,29 @@
* Time: 16:51 * Time: 16:51
*/ */
namespace Hanson\Robot\Message; namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Core\Server; use Hanson\Robot\Core\Server;
use Hanson\Robot\Message\MediaInterface;
use Hanson\Robot\Message\MediaTrait;
use Hanson\Robot\Message\MessageInterface;
use Hanson\Robot\Message\UploadAble;
use Hanson\Robot\Support\Console; use Hanson\Robot\Support\Console;
use Hanson\Robot\Support\FileManager;
class Emoticon extends Message class Emoticon extends Message implements MediaInterface, MessageInterface
{ {
use UploadAble, MediaTrait;
use UploadAble; static $folder = 'gif';
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
public static function send($username, $file) public static function send($username, $file)
{ {
...@@ -28,7 +41,7 @@ class Emoticon extends Message ...@@ -28,7 +41,7 @@ class Emoticon extends Message
$mediaId = $response['MediaId']; $mediaId = $response['MediaId'];
$url = sprintf(Server::BASE_URI . '/webwxsendemoticon?fun=sys&f=json&pass_ticket=%s' , server()->passTicket); $url = sprintf(server()->baseUri . '/webwxsendemoticon?fun=sys&f=json&pass_ticket=%s' , server()->passTicket);
$data = [ $data = [
'BaseRequest'=> server()->baseRequest, 'BaseRequest'=> server()->baseRequest,
'Msg'=> [ 'Msg'=> [
...@@ -50,4 +63,35 @@ class Emoticon extends Message ...@@ -50,4 +63,35 @@ class Emoticon extends Message
return true; return true;
} }
/**
* 根据MsgID发送文件
*
* @param $username
* @param $msgId
* @return mixed
*/
public static function sendByMsgId($username, $msgId)
{
$path = static::getPath(static::$folder);
static::send($username, $path . "/{$msgId}.gif");
}
/**
* 下载文件
*
* @return mixed
*/
public function download()
{
$url = server()->baseUri . sprintf('/webwxgetmsgimg?MsgID=%s&skey=%s', $this->msg['MsgId'], server()->skey);
$content = http()->get($url);
FileManager::download($this->msg['MsgId'].'.gif', $content, static::$folder);
}
public function make()
{
$this->download();
}
} }
\ No newline at end of file \ No newline at end of file
...@@ -6,16 +6,36 @@ ...@@ -6,16 +6,36 @@
* Time: 16:51 * Time: 16:51
*/ */
namespace Hanson\Robot\Message; namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Core\Server;
use Hanson\Robot\Support\Console; use Hanson\Robot\Support\Console;
use Hanson\Robot\Support\FileManager;
use Hanson\Robot\Message\MediaInterface;
use Hanson\Robot\Message\MediaTrait;
use Hanson\Robot\Message\MessageInterface;
use Hanson\Robot\Message\UploadAble;
class Image extends Message class Image extends Message implements MessageInterface, MediaInterface
{ {
use UploadAble; use UploadAble, MediaTrait;
static $folder = 'jpg';
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
public static function sendByMsgId($username, $msgId)
{
$path = static::getPath(static::$folder);
static::send($username, $path . "/{$msgId}.jpg");
}
public static function send($username, $file) public static function send($username, $file)
{ {
...@@ -28,7 +48,7 @@ class Image extends Message ...@@ -28,7 +48,7 @@ class Image extends Message
$mediaId = $response['MediaId']; $mediaId = $response['MediaId'];
$url = sprintf(Server::BASE_URI . '/webwxsendmsgimg?fun=async&f=json&pass_ticket=%s' , server()->passTicket); $url = sprintf(server()->baseUri . '/webwxsendmsgimg?fun=async&f=json&pass_ticket=%s' , server()->passTicket);
$data = [ $data = [
'BaseRequest'=> server()->baseRequest, 'BaseRequest'=> server()->baseRequest,
'Msg'=> [ 'Msg'=> [
...@@ -49,4 +69,16 @@ class Image extends Message ...@@ -49,4 +69,16 @@ class Image extends Message
return true; return true;
} }
public function make()
{
$this->download();
}
public function download()
{
$url = server()->baseUri . sprintf('/webwxgetmsgimg?MsgID=%s&skey=%s', $this->msg['MsgId'], server()->skey);
$content = http()->get($url);
FileManager::download($this->msg['MsgId'].'.jpg', $content, static::$folder);
}
} }
\ No newline at end of file \ No newline at end of file
...@@ -6,9 +6,11 @@ ...@@ -6,9 +6,11 @@
* Time: 21:13 * Time: 21:13
*/ */
namespace Hanson\Robot\Message; namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Message\MessageInterface;
class Location extends Message implements MessageInterface class Location extends Message implements MessageInterface
{ {
...@@ -40,9 +42,7 @@ class Location extends Message implements MessageInterface ...@@ -40,9 +42,7 @@ class Location extends Message implements MessageInterface
*/ */
private function setLocationText() private function setLocationText()
{ {
$result = explode('<br/>', $this->msg['Content']); $this->content = current(explode(":\n", $this->msg['Content']));
$this->content = substr(current($result), 0, -1);
$this->url = $this->msg['Url']; $this->url = $this->msg['Url'];
} }
......
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2016/12/15
* Time: 0:12
*/
namespace Hanson\Robot\Message\Entity;
use Carbon\Carbon;
use Hanson\Robot\Core\Server;
use Hanson\Robot\Collections\Contact;
use Hanson\Robot\Collections\Official;
use Hanson\Robot\Collections\SpecialAccount;
use Hanson\Robot\Support\FileManager;
use Hanson\Robot\Support\Console;
use Hanson\Robot\Support\ObjectAble;
class Message
{
/**
* @var array 消息来源
*/
public $from;
/**
* @var array 当from为群组时,sender为用户发送者
*/
public $sender;
/**
* @var array 消息接收者
*/
public $to;
/**
* @var string 经过处理的内容
*/
public $content;
/**
* @var Carbon 时间
*/
public $time;
/**
* @var string 消息发送者类型
*/
public $fromType;
public $isAt = false;
const USER_TYPE = [
0 => 'Init',
1 => 'Self',
2 => 'FileHelper',
3 => 'Group',
4 => 'Contact',
5 => 'Public',
6 => 'Special',
99 => 'UnKnown',
];
public $msg;
static $mediaCount = -1;
public function __construct($msg)
{
$this->msg = $msg;
$this->setFrom();
$this->setTo();
$this->setFromType();
$this->msg['Content'] = html_entity_decode($this->formatContent($this->msg['Content']));
if($this->fromType === 'Group'){
$this->handleGroupContent($this->msg['Content']);
}
$this->time = $msg['CreateTime'];
}
/**
* 设置消息发送者
*/
private function setFrom()
{
$this->from = account()->getAccount($this->msg['FromUserName']);
}
private function setTo()
{
$this->to = account()->getAccount($this->msg['ToUserName']);
}
private function setFromType()
{
if ($this->msg['MsgType'] == 51) {
$this->fromType = 'System';
} elseif ($this->msg['MsgType'] == 37) {
$this->fromType = 'FriendRequest';
} elseif ($this->msg['FromUserName'] === myself()->username) {
$this->fromType = 'Self';
} elseif ($this->msg['ToUserName'] === 'filehelper') {
$this->fromType = 'FileHelper';
} elseif (substr($this->msg['FromUserName'], 0, 2) === '@@') { # group
$this->fromType = 'Group';
} elseif (contact()->getContactByUsername($this->msg['FromUserName'])) {
$this->fromType = 'Contact';
} elseif (Official::getInstance()->get($this->msg['FromUserName'])) {
$this->fromType = 'Official';
} elseif (SpecialAccount::getInstance()->get($this->msg['FromUserName'], false)) {
$this->fromType = 'Special';
} else {
$this->fromType = 'Unknown';
}
}
/**
* 处理群发消息的内容
*
* @param $content string 内容
*/
private function handleGroupContent($content)
{
if(!$content || !str_contains($content, ":\n")){
return;
}
list($uid, $content) = explode(":\n", $content, 2);
$this->sender = account()->getAccount($uid);
$this->msg['Content'] = $this->formatContent($content);
$this->isAt = str_contains($this->msg['Content'], '@'.myself()->nickname);
}
protected function formatContent($content)
{
return str_replace('<br/>', "\n", $content);
}
/**
* 存储消息到 Message 集合
*/
public function addMessageCollection()
{
message()->put($this->msg['MsgId'], [
'content' => $this->content,
'username' => $this->username,
'sender' => $this->sender,
'msg_type' => $this->msg['MsgType'],
'type' => $this->type,
'created_at' => $this->msg['CreateTime'],
'from_type' => $this->fromType
]);
}
public function __toString()
{
return $this->content;
}
}
\ No newline at end of file \ No newline at end of file
...@@ -6,31 +6,30 @@ ...@@ -6,31 +6,30 @@
* Time: 15:48 * Time: 15:48
*/ */
namespace Hanson\Robot\Message; namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Message\MediaTrait;
use Hanson\Robot\Message\MessageInterface;
class Recall extends Message class Recall extends Message implements MessageInterface
{ {
public $raw; use MediaTrait;
/** /**
* @var string 上一条撤回的msgId * @var Message 上一条撤回的消息
*/ */
public $msgId; public $origin;
public $msg; /**
* @var string 撤回者昵称
public $content; */
public $nickname;
public function __construct(array $msg) public function __construct($msg)
{ {
$this->raw = $msg; parent::__construct($msg);
$this->make();
$msgId = $this->parseMsgId($this->rawMsg['Content']);
$this->msg = $message = message()->get($msgId);
$nickname = $message['sender'] ? $message['sender']['NickName'] : account()->getAccount($message['username'])['NickName'];
$this->content = "{$nickname} 刚撤回了消息 " . $message['type'] === 'Text' ? "\"{$message['content']}\"" : null;
} }
/** /**
...@@ -44,4 +43,20 @@ class Recall extends Message ...@@ -44,4 +43,20 @@ class Recall extends Message
preg_match('/<msgid>(\d+)<\/msgid>/', $xml, $matches); preg_match('/<msgid>(\d+)<\/msgid>/', $xml, $matches);
return $matches[1]; return $matches[1];
} }
public function make()
{
$msgId = $this->parseMsgId($this->msg['Content']);
/** @var Message $message */
$this->origin = $message = message()->get($msgId);
$this->nickname = $message->sender ? $message->sender['NickName'] : account()->getAccount($message->msg['FromUserName'])['NickName'];
$this->setContent();
}
private function setContent()
{
$this->content = "{$this->nickname} 刚撤回了消息";
}
} }
\ No newline at end of file \ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2017/1/15
* Time: 12:29
*/
namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Message\MessageInterface;
class Recommend extends Message implements MessageInterface
{
/**
* @var array 推荐信息
*/
public $info;
public $bigAvatar;
public $smallAvatar;
public $isOfficial = false;
public $description;
/**
* 国内为省,国外为国
*
* @var string
*/
public $province;
/**
* 城市
*
* @var string
*/
public $city;
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
public function make()
{
$this->info = $this->msg['RecommendInfo'];
$this->parseContent();
}
private function parseContent()
{
$isMatch = preg_match('/bigheadimgurl="(http:\/\/.+?)"\ssmallheadimgurl="(http:\/\/.+?)".+province="(.+?)"\scity="(.+?)".+certflag="(\d+)"\scertinfo="(.+?)"/', $this->msg['Content'], $matches);
if($isMatch){
$this->bigAvatar = $matches[1];
$this->smallAvatar = $matches[2];
$this->province = $matches[3];
$this->city = $matches[4];
$flag = $matches[5];
$desc = $matches[6];
if(official()->isOfficial($flag)){
$this->isOfficial = true;
$this->description = $desc;
}
}
}
}
\ No newline at end of file \ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2017/1/15
* Time: 12:29
*/
namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Message\MessageInterface;
class RedPacket extends Message implements MessageInterface
{
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
public function make()
{
$this->content = $this->msg['Content'];
}
}
\ No newline at end of file \ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2017/1/15
* Time: 12:29
*/
namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Message\MessageInterface;
class RequestFriend extends Message implements MessageInterface
{
/**
* @var array 信息
*/
public $info;
public $avatar;
/**
* 国内为省,国外为国
*
* @var string
*/
public $province;
/**
* 城市
*
* @var string
*/
public $city;
const ADD = 2;
const VIA = 3;
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
public function make()
{
$this->info = $this->msg['RecommendInfo'];
$this->parseContent();
}
private function parseContent()
{
$isMatch = preg_match('/province="(.+?)"\scity="(.+?)".+bigheadimgurl="(.+?)"/', $this->msg['Content'], $matches);
if($isMatch){
$this->province = $matches[1];
$this->city = $matches[2];
$this->avatar = $matches[3];
}
}
/**
* 验证通过好友
*
* @param $code
* @param null $ticket
* @return bool
*/
public function verifyUser($code, $ticket = null)
{
$url = sprintf(server()->baseUri.'/webwxverifyuser?lang=zh_CN&r=%s&pass_ticket=%s' ,time()*1000, server()->passTicket);
$data = [
'BaseRequest' => server()->baseRequest,
'Opcode' => $code,
'VerifyUserListSize' => 1,
'VerifyUserList' => [$ticket ? : $this->verifyTicket()],
'VerifyContent' => '',
'SceneListCount' => 1,
'SceneList' => [33],
'skey' => server()->skey
];
$result = http()->json($url, $data, true);
return $result['BaseResponse']['Ret'] == 0;
}
/**
* 返回通过好友申请所需的数组
*
* @return array
*/
public function verifyTicket()
{
return [
'Value' => $this->info['UserName'],
'VerifyUserTicket' => $this->info['Ticket']
];
}
}
\ No newline at end of file \ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2017/1/15
* Time: 12:29
*/
namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Message\MessageInterface;
class Share extends Message implements MessageInterface
{
public $title;
public $description;
public $url;
public $app;
/**
* 转账金额 单位 元
*
* @var string
*/
public $fee;
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
public function make()
{
$array = (array)simplexml_load_string($this->msg['Content'], 'SimpleXMLElement', LIBXML_NOCDATA);
$info = (array)$array['appmsg'];
$this->title = $info['title'];
$this->description = $info['des'];
$appInfo = (array)$array['appinfo'];
$this->app = $appInfo['appname'];
$this->url = $this->msg['Url'];
}
}
\ No newline at end of file \ No newline at end of file
...@@ -6,15 +6,22 @@ ...@@ -6,15 +6,22 @@
* Time: 18:33 * Time: 18:33
*/ */
namespace Hanson\Robot\Message; namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Core\Server; use Hanson\Robot\Message\MessageInterface;
use Hanson\Robot\Support\Console; use Hanson\Robot\Support\Console;
class Text extends Message class Text extends Message implements MessageInterface
{ {
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
/** /**
* 发送消息 * 发送消息
* *
...@@ -22,9 +29,9 @@ class Text extends Message ...@@ -22,9 +29,9 @@ class Text extends Message
* @param $username string 目标username * @param $username string 目标username
* @return bool * @return bool
*/ */
public static function send($username, $word) public static function send($username, string $word)
{ {
if(!$word && !is_string($word)){ if(!$word){
return false; return false;
} }
...@@ -42,8 +49,8 @@ class Text extends Message ...@@ -42,8 +49,8 @@ class Text extends Message
], ],
'Scene' => 0 'Scene' => 0
]; ];
$result = http()->post(Server::BASE_URI . '/webwxsendmsg?pass_ticket=' . server()->passTicket, $result = http()->post(server()->baseUri . '/webwxsendmsg?pass_ticket=' . server()->passTicket,
json_encode($data, JSON_UNESCAPED_UNICODE), true json_encode($data, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES), true
); );
if($result['BaseResponse']['Ret'] != 0){ if($result['BaseResponse']['Ret'] != 0){
...@@ -53,4 +60,9 @@ class Text extends Message ...@@ -53,4 +60,9 @@ class Text extends Message
return true; return true;
} }
public function make()
{
$this->content = $this->msg['Content'];
}
} }
\ No newline at end of file \ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2017/1/15
* Time: 12:29
*/
namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Message\MessageInterface;
class Touch extends Message implements MessageInterface
{
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
public function make()
{
}
}
\ No newline at end of file \ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2017/1/15
* Time: 12:29
*/
namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Message\MessageInterface;
class Transfer extends Message implements MessageInterface
{
/**
* 转账金额 单位 元
*
* @var string
*/
public $fee;
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
public function make()
{
$array = (array)simplexml_load_string($this->msg['Content'], 'SimpleXMLElement', LIBXML_NOCDATA);
$des = (array)$array['appmsg']->des;
$fee = (array)$array['appmsg']->wcpayinfo;
$this->content = current($des);
$this->fee = substr($fee['feedesc'], 3);
}
}
\ No newline at end of file \ No newline at end of file
...@@ -6,15 +6,28 @@ ...@@ -6,15 +6,28 @@
* Time: 22:08 * Time: 22:08
*/ */
namespace Hanson\Robot\Message; namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Core\Server; use Hanson\Robot\Message\MediaInterface;
use Hanson\Robot\Message\MediaTrait;
use Hanson\Robot\Message\MessageInterface;
use Hanson\Robot\Message\UploadAble;
use Hanson\Robot\Support\Console; use Hanson\Robot\Support\Console;
use Hanson\Robot\Support\FileManager;
class Video extends Message class Video extends Message implements MessageInterface, MediaInterface
{ {
use UploadAble; use UploadAble, MediaTrait;
static $folder = 'mp4';
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
public static function send($username, $file) public static function send($username, $file)
{ {
...@@ -27,7 +40,7 @@ class Video extends Message ...@@ -27,7 +40,7 @@ class Video extends Message
$mediaId = $response['MediaId']; $mediaId = $response['MediaId'];
$url = sprintf(Server::BASE_URI . '/webwxsendvideomsg?fun=async&f=json&pass_ticket=%s' , server()->passTicket); $url = sprintf(server()->baseUri . '/webwxsendvideomsg?fun=async&f=json&pass_ticket=%s' , server()->passTicket);
$data = [ $data = [
'BaseRequest'=> server()->baseRequest, 'BaseRequest'=> server()->baseRequest,
'Msg'=> [ 'Msg'=> [
...@@ -48,4 +61,39 @@ class Video extends Message ...@@ -48,4 +61,39 @@ class Video extends Message
return true; return true;
} }
/**
* 根据MsgID发送文件
*
* @param $username
* @param $msgId
* @return mixed
*/
public static function sendByMsgId($username, $msgId)
{
$path = static::getPath(static::$folder);
static::send($username, $path . "/{$msgId}.mp4");
}
/**
* 下载文件
*
* @return mixed
*/
public function download()
{
$url = server()->baseUri . sprintf('/webwxgetvideo?msgid=%s&skey=%s', $this->msg['MsgId'], server()->skey);
$content = http()->request($url, 'get', [
'headers' => [
'Range' => 'bytes=0-'
]
]);
FileManager::download($this->msg['MsgId'].'.mp4', $content, static::$folder);
}
public function make()
{
$this->download();
}
} }
\ No newline at end of file \ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2017/1/13
* Time: 22:08
*/
namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Message\MediaInterface;
use Hanson\Robot\Message\MediaTrait;
use Hanson\Robot\Message\MessageInterface;
use Hanson\Robot\Message\UploadAble;
use Hanson\Robot\Support\FileManager;
class Voice extends Message implements MessageInterface, MediaInterface
{
use UploadAble, MediaTrait;
static $folder = 'mp3';
public function __construct($msg)
{
parent::__construct($msg);
$this->make();
}
/**
* 根据MsgID发送文件
*
* @param $username
* @param $msgId
* @return mixed
*/
public static function sendByMsgId($username, $msgId)
{
}
/**
* 下载文件
*
* @return mixed
*/
public function download()
{
$url = server()->baseUri . sprintf('/webwxgetvoice?msgid=%s&skey=%s', $this->msg['MsgId'], server()->skey);
$content = http()->get($url);
FileManager::download($this->msg['MsgId'].'.mp3', $content, static::$folder);
}
public function make()
{
}
}
\ No newline at end of file \ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2017/1/15
* Time: 2:53
*/
namespace Hanson\Robot\Message;
interface MediaInterface
{
/**
* 根据MsgID发送文件
*
* @param $username
* @param $msgId
* @return mixed
*/
public static function sendByMsgId($username, $msgId);
/**
* 下载文件
*
* @return mixed
*/
public function download();
}
\ No newline at end of file \ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2017/1/15
* Time: 3:20
*/
namespace Hanson\Robot\Message;
/**
* Class MediaTrait
* @property string $folder
* @package Hanson\Robot\Message
*/
trait MediaTrait
{
public function getPath($folder)
{
$path = server()->config['tmp'] . $folder;
return realpath($path);
}
}
\ No newline at end of file \ No newline at end of file
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2016/12/15
* Time: 0:12
*/
namespace Hanson\Robot\Message;
use Carbon\Carbon;
use Hanson\Robot\Core\Server;
use Hanson\Robot\Collections\Contact;
use Hanson\Robot\Collections\OfficialAccount;
use Hanson\Robot\Collections\SpecialAccount;
use Hanson\Robot\Support\FileManager;
use Hanson\Robot\Support\Console;
use Hanson\Robot\Support\ObjectAble;
class Message
{
use ObjectAble;
/**
* @var array 消息来源
*/
public $from;
/**
* @var array 当from为群组时,sender为用户发送者
*/
public $sender;
/**
* @var string 来源的username,用于回复
*/
public $username;
/**
* @var array 消息接收者
*/
public $to;
/**
* @var string 经过处理的内容
*/
public $content;
/**
* @var Carbon 时间
*/
public $time;
/**
* @var string 消息发送者类型
*/
public $fromType;
/**
* @var string 消息类型
*/
public $type;
public $isAt = false;
const USER_TYPE = [
0 => 'Init',
1 => 'Self',
2 => 'FileHelper',
3 => 'Group',
4 => 'Contact',
5 => 'Public',
6 => 'Special',
99 => 'UnKnown',
];
public $rawMsg;
public $msg;
static $mediaCount = -1;
public function __construct($msg)
{
$this->msg = $msg;
$this->setFrom();
$this->setTo();
}
// public function make($selector, $msg)
// {
// $this->rawMsg = $msg;
// $this->time = Carbon::now();
//
// $this->setFrom();
// $this->setTo();
// $this->setFromType();
// $this->setType();
// $this->rawMsg['selector'] = $selector;
// $this->addMessageCollection();
// return $this;
// }
/**
* 设置消息发送者
*/
private function setFrom()
{
$from = account()->getAccount($this->msg['FromUserName']);
$this->from = $this->toObject($from);
}
private function setTo()
{
$to = account()->getAccount($this->msg['ToUserName']);
print_r($to);
$this->to = $this->toObject($to);
}
private function setFromType()
{
if ($this->rawMsg['MsgType'] == 51) {
$this->fromType = 'System';
} elseif ($this->rawMsg['MsgType'] == 37) {
$this->fromType = 'FriendRequest';
} elseif ($this->rawMsg['FromUserName'] === myself()->username) {
$this->fromType = 'Self';
} elseif ($this->rawMsg['ToUserName'] === 'filehelper') {
$this->fromType = 'FileHelper';
} elseif (substr($this->rawMsg['FromUserName'], 0, 2) === '@@') { # group
$this->fromType = 'Group';
} elseif (contact()->getContactByUsername($this->rawMsg['FromUserName'])) {
$this->fromType = 'Contact';
} elseif (OfficialAccount::getInstance()->isPublic($this->rawMsg['FromUserName'])) {
$this->fromType = 'Official';
} elseif (SpecialAccount::getInstance()->get($this->rawMsg['FromUserName'], false)) {
$this->fromType = 'Special';
} else {
$this->fromType = 'Unknown';
}
}
private function setType()
{
$this->rawMsg['Content'] = html_entity_decode($this->rawMsg['Content']);
$this->setTypeByFrom();
$this->handleMessageByType();
}
/**
* 根据消息来源处理消息
*/
private function setTypeByFrom()
{
if($this->fromType === 'System'){
$this->type = 'Empty';
}elseif ($this->fromType === 'FileHelper'){ # File Helper
$this->type = 'Text';
$this->content = $this->formatContent($this->rawMsg['Content']);
}elseif ($this->fromType === 'Group'){
$this->handleGroupContent($this->rawMsg['Content']);
}
}
/**
* 处理消息类型
*/
private function handleMessageByType()
{
switch($this->rawMsg['MsgType']){
case 1: //文本消息
if(Location::isLocation($this->rawMsg)){
$this->type = 'Location';
$this->content = Location::getLocationText($this->rawMsg['Content']);
}else{
$this->type = 'Text';
$this->content = $this->rawMsg['Content'];
}
break;
case 3: // 图片消息
$this->type = 'Image';
$this->content = Server::BASE_URI . sprintf('/webwxgetmsgimg?MsgID=%s&skey=%s', $this->rawMsg['MsgId'], server()->skey);
$content = http()->get($this->content);
FileManager::download($this->rawMsg['MsgId'].'.jpg', $content, 'jpg');
break;
case 34: // 语音消息
$this->type = 'Voice';
$this->content = Server::BASE_URI . sprintf('/webwxgetvoice?msgid=%s&skey=%s', $this->rawMsg['MsgId'], server()->skey);
$content = http()->get($this->content);
FileManager::download($this->rawMsg['MsgId'].'.mp3', $content, 'mp3');
break;
case 37: // 好友验证
$this->type = 'AddUser';
break;
case 42: //共享名片
$this->type = 'Recommend';
$this->content = (object)$this->rawMsg['RecommendInfo'];
break;
case 43:
$this->type = 'VideoCall';
Console::log('video');
$url = Server::BASE_URI . sprintf('/webwxgetvideo?msgid=%s&skey=%s', $this->rawMsg['MsgId'], server()->skey);
$content = http()->request($url, 'get', [
'headers' => [
'Range' => 'bytes=0-'
]
]);
FileManager::download($this->rawMsg['MsgId'].'.mp4', $content, 'mp4');
break;
case 47: // 动画表情
$this->type = 'Emoticon';
$this->content = Server::BASE_URI . sprintf('/webwxgetmsgimg?MsgID=%s&skey=%s', $this->rawMsg['MsgId'], server()->skey);
$content = http()->get($this->content);
FileManager::download($this->rawMsg['MsgId'].'.gif', $content, 'gif');
break;
case 49:
$this->type = 'Share';
break;
case 62:
$this->type = 'Video';
Console::log('video');
$url = Server::BASE_URI . sprintf('/webwxgetvideo?msgid=%s&skey=%s', $this->rawMsg['MsgId'], server()->skey);
$content = http()->request($url, 'get', [
'headers' => [
'Range' => 'bytes=0-'
]
]);
FileManager::download($this->rawMsg['MsgId'].'.mp4', $content, 'mp4');
break;
case 51:
$this->type = 'Init';
break;
case 53:
$this->type = 'VideoCall';
break;
case 10000:
if($this->rawMsg['Status'] == 4){
$this->type = 'RedPacket'; // 红包
}else{
$this->type = 'Unknown';
}
break;
case 10002:
$this->type = 'Recall'; // 撤回
$this->msgId = $msgId = $this->parseMsgId($this->rawMsg['Content']);
break;
default:
$this->type = 'Unknown';
break;
}
}
/**
* 处理群发消息的内容
*
* @param $content string 内容
*/
private function handleGroupContent($content)
{
if(!$content || !str_contains($content, '<br/>')){
return;
}
list($uid, $content) = explode('<br/>', $content, 2);
$this->sender = member()->getMemberByUsername(substr($uid, 0, -1));
$this->rawMsg['Content'] = $this->formatContent($content);
$this->isAt = str_contains($this->rawMsg['Content'], '@'.myself()->nickname);
}
private function formatContent($content)
{
return str_replace('<br/>', "\n", $content);
}
/**
* 解析message获取msgId
*
* @param $xml
* @return string msgId
*/
private function parseMsgId($xml)
{
preg_match('/<msgid>(\d+)<\/msgid>/', $xml, $matches);
return $matches[1];
}
/**
* 存储消息到 Message 集合
*/
public function addMessageCollection()
{
message()->put($this->rawMsg['MsgId'], [
'content' => $this->content,
'username' => $this->username,
'sender' => $this->sender,
'msg_type' => $this->rawMsg['MsgType'],
'type' => $this->type,
'created_at' => $this->rawMsg['CreateTime'],
'from_type' => $this->fromType
]);
}
/**
* 发送消息
*
* @param $word string 消息内容
* @param $username string 目标username
* @return bool
*/
public static function send(string $word, $username)
{
if(!$word && !is_string($word)){
return false;
}
Console::log($word);
$random = strval(time() * 1000) . '0' . strval(rand(100, 999));
$data = [
'BaseRequest' => server()->baseRequest,
'Msg' => [
'Type' => 1,
'Content' => $word,
'FromUserName' => myself()->username,
'ToUserName' => $username,
'LocalID' => $random,
'ClientMsgId' => $random,
],
'Scene' => 0
];
$result = http()->post(Server::BASE_URI . '/webwxsendmsg?pass_ticket=' . server()->passTicket,
json_encode($data, JSON_UNESCAPED_UNICODE), true
);
if($result['BaseResponse']['Ret'] != 0){
Console::log('发送消息失败');
return false;
}
return true;
}
public function __toString()
{
return $this->content;
}
}
\ No newline at end of file \ No newline at end of file
...@@ -85,7 +85,7 @@ trait UploadAble ...@@ -85,7 +85,7 @@ trait UploadAble
$mediaId = $response['MediaId']; $mediaId = $response['MediaId'];
$url = sprintf(Server::BASE_URI . '/webwxsendappmsg?fun=async&f=json' , server()->passTicket); $url = sprintf(server()->baseUri . '/webwxsendappmsg?fun=async&f=json' , server()->passTicket);
$data = [ $data = [
'BaseRequest'=> server()->baseRequest, 'BaseRequest'=> server()->baseRequest,
'Msg'=> [ 'Msg'=> [
...@@ -97,7 +97,6 @@ trait UploadAble ...@@ -97,7 +97,6 @@ trait UploadAble
'ClientMsgId'=> time() * 1e4 'ClientMsgId'=> time() * 1e4
] ]
]; ];
print_r($data);
$result = http()->json($url, $data, true); $result = http()->json($url, $data, true);
if($result['BaseResponse']['Ret'] != 0){ if($result['BaseResponse']['Ret'] != 0){
......
<?php
/**
* Created by PhpStorm.
* User: Hanson
* Date: 2017/1/13
* Time: 22:08
*/
namespace Hanson\Robot\Message;
class Voice extends Message
{
use UploadAble;
}
\ No newline at end of file \ No newline at end of file
...@@ -14,6 +14,7 @@ use Hanson\Robot\Collections\Member; ...@@ -14,6 +14,7 @@ use Hanson\Robot\Collections\Member;
use Hanson\Robot\Collections\Contact; use Hanson\Robot\Collections\Contact;
use Hanson\Robot\Collections\Message; use Hanson\Robot\Collections\Message;
use Hanson\Robot\Collections\Group; use Hanson\Robot\Collections\Group;
use Hanson\Robot\Collections\Official;
if (! function_exists('server')) { if (! function_exists('server')) {
/** /**
...@@ -104,3 +105,14 @@ if (! function_exists('message')) { ...@@ -104,3 +105,14 @@ if (! function_exists('message')) {
return Message::getInstance(); return Message::getInstance();
} }
} }
if (! function_exists('official')) {
/**
* Get the available container instance.
*
* @return Official
*/
function official()
{
return Official::getInstance();
}
}
\ No newline at end of file \ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!