Commit c77778a2 by HanSon

优化消息

1 parent 540f3f4e
......@@ -296,62 +296,61 @@ $robot->server->setCustomHandler(function(){
# 特别感谢
[liuwons/wxBot](https://github.com/liuwons/wxBot) 参考了整个微信的登录流程与消息处理
[overtrue/wechat](https://github.com/overtrue/wechat) 参考了部分代码的书写格式与设计思路
[lbbniu/WebWechat](https://github.com/lbbniu/WebWechat)
# to do list
[littlecodersh/ItChat](https://github.com/littlecodersh/ItChat)
- [ ] 命令行操作信息发送
感谢以上两位作者曾对本人耐心解答
- [ ] 抽象消息
- [ ] 图片
- [ ] 视频
- [ ] 文字
- [ ] 语音
- [ ] 表情
## 参考项目
- [ ] 群操作
- [ ] 创建群
- [ ] 把某人踢出群
- [ ] 邀请好友加入群
- [ ] 修改群名称
- [ ] 聊天窗口操作
- [ ] 置顶聊天会话
- [ ] 取消聊天会话指定
- [ ] 好友操作
- [ ] 给好友添加备注
- [ ] 通过好友验证
[liuwons/wxBot](https://github.com/liuwons/wxBot) 参考了整个微信的登录流程与消息处理
- [x] 增加消息集合存储
# to do list
- [x] 消息发送
- [x] 发送文字
- [x] 发送图片
- [x] 发送表情
- [x] 发送视频
- [x] 消息存储
- [x] 语音
- [x] 图片
- [x] 视频
- [x] 表情
- [ ] 消息处理
- [x] 文字
- [x] 图片
- [x] 语音
- [x] 位置
- [x] 视频
- [x] 撤回
- [x] 表情
- [x] 红包
- [x] 转账
- [x] 名片
- [ ] 好友验证
- [ ] 名片
- [ ] 分享
- [ ] 视频
- [ ] 小程序
- [x] 消息存储
- [x] 语音
- [x] 图片
- [x] 视频
- [x] 表情
- [x] 消息发送
- [x] 发送文字
- [x] 发送图片
- [x] 发送表情
- [x] 发送视频
- [ ] 群操作
- [ ] 创建群
- [ ] 把某人踢出群
- [ ] 邀请好友加入群
- [ ] 修改群名称
- [ ] 好友操作
- [ ] 给好友添加备注
- [ ] 通过好友验证
- [ ] 聊天窗口操作
- [ ] 置顶聊天会话
- [ ] 取消聊天会话指定
- [ ] 命令行操作信息发送
# 已知bug
* 30% 的几率初始化失败(暂时无解,如清楚问题欢迎PR)
......@@ -9,7 +9,7 @@
require_once __DIR__ . './../vendor/autoload.php';
use Hanson\Robot\Foundation\Robot;
use Hanson\Robot\Message\Message;
use Hanson\Robot\Message\Text;
$robot = new Robot([
'tmp' => __DIR__ . '/./../tmp/',
......@@ -18,14 +18,13 @@ $robot = new Robot([
$flag = false;
$robot->server->setCustomerHandler(function() use (&$flag){
/** @var $message Message */
if(!$flag){
Message::send('custom', contact()->getUsernameById('L907159127'));
Text::send('custom', contact()->getUsernameById('L907159127'));
$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){
if($message->type === 'Text'){
/** @var $message Message */
$contact = contact()->getUsernameById('hanson1994');
Message::send($message->content, $contact);
Text::send($message->content, $contact);
}
});
$robot->server->setCustomerHandler(function(){
......
......@@ -18,7 +18,7 @@ $robot = new Robot([
$robot->server->setMessageHandler(function($message){
/** @var $message Message */
if($message->type === 'Text'){
Message::send($message->fromType, $message->username);
Text::send($message->fromType, $message->username);
}
});
$robot->server->run();
......@@ -9,7 +9,7 @@
require_once __DIR__ . './../vendor/autoload.php';
use Hanson\Robot\Foundation\Robot;
use Hanson\Robot\Message\Message;
use Hanson\Robot\Message\Text;
$robot = new Robot([
'tmp' => __DIR__ . '/./../tmp/',
......@@ -17,10 +17,9 @@ $robot = new Robot([
]);
$robot->server->setCustomerHandler(function(){
/** @var $message Message */
$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(){
$groups = group()->getGroupsByNickname('stackoverflow', true);
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){
}
if($message->type === 'Recall' && $message->rawMsg['FromUserName'] !== myself()->username){
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'));
}
......
......@@ -9,13 +9,20 @@
require_once __DIR__ . './../vendor/autoload.php';
use Hanson\Robot\Foundation\Robot;
use Hanson\Robot\Message\Message;
use Hanson\Robot\Message\Image;
use Hanson\Robot\Message\Text;
use Hanson\Robot\Message\Emoticon;
use Hanson\Robot\Message\Location;
use Hanson\Robot\Message\Video;
use Hanson\Robot\Message\MessageInterface;
use Hanson\Robot\Message\Entity\Message;
use Hanson\Robot\Message\Entity\Image;
use Hanson\Robot\Message\Entity\Text;
use Hanson\Robot\Message\Entity\Emoticon;
use Hanson\Robot\Message\Entity\Location;
use Hanson\Robot\Message\Entity\Video;
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/';
$robot = new Robot([
......@@ -23,36 +30,127 @@ $robot = new Robot([
'debug' => true
]);
$robot->server->setMessageHandler(function($message) use ($path){
$robot->server->setMessageHandler(function ($message) use ($path) {
/** @var $message Message */
// 位置信息 返回位置文字
if($message instanceof Location){
if ($message instanceof Location) {
return $message;
}
// 发送撤回消息 (排除自己)
// if($message->type === 'Recall' && $message->rawMsg['FromUserName'] !== myself()->username && $message->username === group()->getGroupsByNickname('stackoverflow', true)->first()['UserName']){
// $msg = message()->get($message->msgId);
// if($msg){
// $nickname = $msg['sender'] ? $msg['sender']['NickName'] : account()->getAccount($msg['username'])['NickName'];
// if($msg['type'] === 'Image'){
// Text::send($message->username, "{$nickname} 撤回了一张照片");
// Image::send($message->username, realpath($path . "jpg/{$message->msgId}.jpg"));
// }elseif($msg['type'] === 'Emoticon'){
// Text::send($message->username, "{$nickname} 撤回了一个表情");
// Emoticon::send($message->username, realpath($path . "gif/{$message->msgId}.gif"));
// }elseif($msg['type'] === 'Video' || $msg['type'] === 'VideoCall'){
// Text::send($message->username, "{$nickname} 撤回了一个视频");
// Video::send($message->username, realpath($path . "mp4/{$message->msgId}.mp4"));
// }elseif($msg['type'] === 'Voice'){
// Text::send($message->username, "{$nickname} 撤回了一条语音");
// }else{
// Text::send($message->username, "{$nickname} 撤回了一条信息 \"{$msg['content']}\"");
// }
// }
// 文字信息
if ($message instanceof Text) {
// 联系人自动回复
if ($message->fromType === 'Contact') {
return http()->post('http://www.tuling123.com/openapi/api', [
'key' => '1dce02aef026258eff69635a06b0ab7d',
'info' => $message->content
], true)['text'];
// 群组@我回复
} elseif ($message->fromType === 'Group' && $message->isAt) {
return http()->post('http://www.tuling123.com/openapi/api', [
'key' => '1dce02aef026258eff69635a06b0ab7d',
'info' => $message->content
], true)['text'];
}
}
// 图片信息 返回接收到的图片
// 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();
......@@ -55,7 +55,7 @@ class Contact extends Collection
public function getContactById($id)
{
$contact = $this->filter(function($item, $key) use ($id){
if($item->Alias === $id){
if($item['Alias'] === $id){
return true;
}
})->first();
......@@ -72,7 +72,7 @@ class Contact extends Collection
public function getUsernameById($id)
{
$contact = $this->search(function($item, $key) use ($id){
if($item->Alias === $id){
if($item['Alias'] === $id){
return true;
}
});
......
......@@ -11,11 +11,9 @@ namespace Hanson\Robot\Collections;
use Hanson\Robot\Core\Server;
use Hanson\Robot\Support\Console;
use Hanson\Robot\Support\ObjectAble;
class ContactFactory
{
use ObjectAble;
const SPECIAL_USERS = ['newsapp', 'fmessage', 'filehelper', 'weibo', 'qqmail',
'fmessage', 'tmessage', 'qmessage', 'qqsync', 'floatbottle',
......@@ -33,7 +31,7 @@ class ContactFactory
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, [
'BaseRequest' => server()->baseRequest
......@@ -50,14 +48,14 @@ class ContactFactory
protected function makeContactList($memberList)
{
foreach ($memberList as $contact) {
if(($contact['VerifyFlag'] & 8) != 0){ #公众号
OfficialAccount::getInstance()->put($contact['UserName'], $this->toObject($contact));
if(official()->isOfficial($contact['VerifyFlag'])){ #公众号
Official::getInstance()->put($contact['UserName'], $contact);
}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){ # 群聊
group()->put($contact['UserName'], $this->toObject($contact));
group()->put($contact['UserName'], $contact);
}else{
contact()->put($contact['UserName'], $this->toObject($contact));
contact()->put($contact['UserName'], $contact);
}
}
......@@ -66,7 +64,7 @@ class ContactFactory
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'] . '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()));
}
}
......@@ -76,7 +74,7 @@ class ContactFactory
*/
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 = [];
group()->each(function($item, $key) use (&$list){
......@@ -103,9 +101,9 @@ class ContactFactory
$groupAccount = group()->get($group['UserName']);
$groupAccount['MemberList'] = $group['MemberList'];
$groupAccount['ChatRoomId'] = $group['EncryChatRoomId'];
group()->put($group['UserName'], $this->toObject($groupAccount));
group()->put($group['UserName'], $groupAccount);
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
* @param bool $onlyUsername
* @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){
if(!$blur){
return $value->NickName === $name;
return $value['NickName'] === $name;
}else{
return str_contains($value->NickName, $name);
return str_contains($value['NickName'], $name);
}
});
if($onlyUsername){
$groups = $groups->only(['UserName']);
}
return $groups;
}
......
......@@ -11,31 +11,31 @@ namespace Hanson\Robot\Collections;
use Illuminate\Support\Collection;
class OfficialAccount extends Collection
class Official extends Collection
{
/**
* @var OfficialAccount
* @var Official
*/
static $instance = null;
/**
* create a single instance
*
* @return OfficialAccount
* @return Official
*/
public static function getInstance()
{
if(static::$instance === null){
static::$instance = new OfficialAccount();
static::$instance = new Official();
}
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
......@@ -9,9 +9,20 @@
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\FileManager;
class MessageFactory
{
......@@ -32,82 +43,51 @@ class MessageFactory
*/
private function handleMessageByType()
{
Console::log($this->msg['MsgType']);
switch($this->msg['MsgType']){
case 1: //文本消息
if(Location::isLocation($this->msg)){
return new Location($this->msg);
}else{
$this->type = 'Text';
$this->content = $this->msg['Content'];
return new Text($this->msg);
}
break;
case 3: // 图片消息
$this->type = 'Image';
$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;
return new Image($this->msg);
case 34: // 语音消息
$this->type = 'Voice';
$this->content = Server::BASE_URI . sprintf('/webwxgetvoice?msgid=%s&skey=%s', $this->msg['MsgId'], server()->skey);
$content = http()->get($this->content);
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;
return new Voice($this->msg);
case 43: // 视频
return new Video($this->msg);
case 47: // 动画表情
$this->type = 'Emoticon';
$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'].'.gif', $content, 'gif');
return new Emoticon($this->msg);
case 10002:
return new Recall($this->msg);
case 10000:
if($this->msg['Status'] == 4){
return new RedPacket($this->msg);
}else{
}
break;
case 49:
$this->type = 'Share';
break;
if($this->msg['Status'] == 3 && $this->msg['FileName'] === '微信转账'){
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:
$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;
case 51:
$this->type = 'Init';
if($this->msg['ToUserName'] === $this->msg['StatusNotifyUserName']){
return new Touch($this->msg);
}
break;
case 53:
$this->type = 'VideoCall';
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:
$this->type = 'Unknown';
break;
......
......@@ -10,7 +10,11 @@ namespace Hanson\Robot\Core;
use Closure;
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;
class MessageHandler
......@@ -120,15 +124,36 @@ class MessageHandler
if($message['AddMsgList']){
foreach ($message['AddMsgList'] as $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){
$reply = call_user_func_array($this->handler, [$content]);
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
......@@ -22,8 +22,6 @@ use Symfony\Component\DomCrawler\Crawler;
class Server
{
use ObjectAble;
static $instance;
protected $uuid;
......@@ -52,9 +50,13 @@ class Server
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;
public $pushUri;
const BASE_HOST = 'wx.qq.com';
const BASE_HOST = 'wx2.qq.com';
public function __construct($config = [])
{
......@@ -173,6 +175,26 @@ class Server
preg_match('/window.redirect_uri="(\S+?)";/', $content, $matches);
$this->redirectUri = $matches[1] . '&fun=new';
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;
case '408':
Console::log('[ERROR] login timeout. please try 1 second later.');
......@@ -226,7 +248,7 @@ class Server
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, [
'BaseRequest' => $this->baseRequest
......@@ -242,7 +264,7 @@ class Server
if($result['BaseResponse']['Ret'] != 0){
// print_r($this->baseRequest);
// Console::log('init URL:'. $url);
Console::log('init URL:'. $url);
throw new Exception('[ERROR] init fail!');
}
}
......@@ -252,7 +274,7 @@ class Server
if($contactList){
foreach ($contactList as $contact) {
if(Group::isGroup($contact['UserName'])){
group()->put($contact['UserName'], $this->toObject($contact));
group()->put($contact['UserName'], $contact);
}
}
}
......@@ -268,7 +290,7 @@ class Server
*/
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, [
'BaseRequest' => $this->baseRequest,
......
......@@ -44,7 +44,7 @@ class 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{
$result = http()->json($url, [
......@@ -74,8 +74,10 @@ class Sync
$syncKey = [];
foreach (server()->syncKey['List'] as $item) {
$syncKey[] = $item['Key'] . '_' . $item['Val'];
if(is_array(server()->syncKey['List'])){
foreach (server()->syncKey['List'] as $item) {
$syncKey[] = $item['Key'] . '_' . $item['Val'];
}
}
server()->syncKeyStr = implode('|', $syncKey);
......
......@@ -6,16 +6,29 @@
* Time: 16:51
*/
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\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)
{
......@@ -28,7 +41,7 @@ class Emoticon extends Message
$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 = [
'BaseRequest'=> server()->baseRequest,
'Msg'=> [
......@@ -50,4 +63,35 @@ class Emoticon extends Message
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
......@@ -6,16 +6,36 @@
* 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\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)
{
......@@ -28,7 +48,7 @@ class Image extends Message
$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 = [
'BaseRequest'=> server()->baseRequest,
'Msg'=> [
......@@ -49,4 +69,16 @@ class Image extends Message
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
......@@ -6,9 +6,11 @@
* Time: 21:13
*/
namespace Hanson\Robot\Message;
namespace Hanson\Robot\Message\Entity;
use Hanson\Robot\Message\MessageInterface;
class Location extends Message implements MessageInterface
{
......@@ -40,9 +42,7 @@ class Location extends Message implements MessageInterface
*/
private function setLocationText()
{
$result = explode('<br/>', $this->msg['Content']);
$this->content = substr(current($result), 0, -1);
$this->content = current(explode(":\n", $this->msg['Content']));
$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
......@@ -6,31 +6,30 @@
* 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;
public $content;
/**
* @var string 撤回者昵称
*/
public $nickname;
public function __construct(array $msg)
public function __construct($msg)
{
$this->raw = $msg;
parent::__construct($msg);
$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;
$this->make();
}
/**
......@@ -44,4 +43,20 @@ class Recall extends Message
preg_match('/<msgid>(\d+)<\/msgid>/', $xml, $matches);
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
<?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
<?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
<?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
<?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
......@@ -6,15 +6,22 @@
* 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;
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
* @param $username string 目标username
* @return bool
*/
public static function send($username, $word)
public static function send($username, string $word)
{
if(!$word && !is_string($word)){
if(!$word){
return false;
}
......@@ -42,8 +49,8 @@ class Text extends Message
],
'Scene' => 0
];
$result = http()->post(Server::BASE_URI . '/webwxsendmsg?pass_ticket=' . server()->passTicket,
json_encode($data, JSON_UNESCAPED_UNICODE), true
$result = http()->post(server()->baseUri . '/webwxsendmsg?pass_ticket=' . server()->passTicket,
json_encode($data, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES), true
);
if($result['BaseResponse']['Ret'] != 0){
......@@ -53,4 +60,9 @@ class Text extends Message
return true;
}
public function make()
{
$this->content = $this->msg['Content'];
}
}
\ 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
<?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
......@@ -6,15 +6,28 @@
* 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\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)
{
......@@ -27,7 +40,7 @@ class Video extends Message
$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 = [
'BaseRequest'=> server()->baseRequest,
'Msg'=> [
......@@ -48,4 +61,39 @@ class Video extends Message
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
<?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
<?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
<?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
<?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
......@@ -85,7 +85,7 @@ trait UploadAble
$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 = [
'BaseRequest'=> server()->baseRequest,
'Msg'=> [
......@@ -97,7 +97,6 @@ trait UploadAble
'ClientMsgId'=> time() * 1e4
]
];
print_r($data);
$result = http()->json($url, $data, true);
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
......@@ -14,6 +14,7 @@ use Hanson\Robot\Collections\Member;
use Hanson\Robot\Collections\Contact;
use Hanson\Robot\Collections\Message;
use Hanson\Robot\Collections\Group;
use Hanson\Robot\Collections\Official;
if (! function_exists('server')) {
/**
......@@ -103,4 +104,15 @@ if (! function_exists('message')) {
{
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
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!