feat: 新增机器人

This commit is contained in:
kuaifan 2022-12-17 12:41:25 +08:00
parent 090ce1cf18
commit 98b6466dd5
37 changed files with 652 additions and 85 deletions

View File

@ -592,13 +592,15 @@ class DialogController extends AbstractController
Base::checkClientVersion('0.19.0');
$user = User::auth();
//
$chat_information = Base::settingFind('system', 'chat_information');
if ($chat_information == 'required') {
if (empty($user->getRawOriginal('nickname'))) {
return Base::retError('请设置昵称', [], -2);
}
if (empty($user->getRawOriginal('tel'))) {
return Base::retError('请设置联系电话', [], -3);
if (!$user->bot) {
$chatInformation = Base::settingFind('system', 'chat_information');
if ($chatInformation == 'required') {
if (empty($user->getRawOriginal('nickname'))) {
return Base::retError('请设置昵称', [], -2);
}
if (empty($user->getRawOriginal('tel'))) {
return Base::retError('请设置联系电话', [], -3);
}
}
}
//

View File

@ -365,7 +365,8 @@ class UsersController extends AbstractController
*
* @apiParam {Object} keys 搜索条件
* - keys.key 昵称、邮箱关键字
* - keys.disable 0-排除禁止默认1-含禁止2-仅禁止
* - keys.disable 0-排除禁止默认1-仅禁止2-含禁止
* - keys.bot 0-排除机器人默认1-仅机器人2-含机器人
* - keys.project_id 在指定项目ID
* - keys.no_project_id 不在指定项目ID
* - keys.dialog_id 在指定对话ID
@ -403,9 +404,14 @@ class UsersController extends AbstractController
}
if (intval($keys['disable']) == 0) {
$builder->whereNull("disable_at");
} elseif (intval($keys['disable']) == 2) {
} elseif (intval($keys['disable']) == 1) {
$builder->whereNotNull("disable_at");
}
if (intval($keys['bot']) == 0) {
$builder->where("bot", 0);
} elseif (intval($keys['bot']) == 1) {
$builder->where("bot", 1);
}
if ($updatedTime > 0) {
$builder->where("updated_at", ">=", Carbon::createFromTimestamp($updatedTime));
}
@ -505,6 +511,10 @@ class UsersController extends AbstractController
* - yes: 已认证
* - no: 未认证
* - 其他值: 全部(默认)
* - keys.bot 是否包含机器人
* - yes: 仅机器人
* - all: 全部
* - 其他值: 非机器人(默认)
* - keys.department 部门ID0表示默认部门不赋值获取所有部门
* - keys.checkin_mac 签到mac地址get_checkin_mac=1时有效)
*
@ -569,6 +579,11 @@ class UsersController extends AbstractController
} elseif ($keys['email_verity'] === 'no') {
$builder->whereEmailVerity(0);
}
if ($keys['bot'] === 'yes') {
$builder->where('bot', 1);
} elseif ($keys['bot'] !== 'all') {
$builder->where('bot', 0);
}
if (isset($keys['department'])) {
if ($keys['department'] == '0') {
$builder->where(function($query) {
@ -585,6 +600,7 @@ class UsersController extends AbstractController
}
} else {
$builder->whereNull('disable_at');
$builder->where('bot', 0);
}
$list = $builder->orderByDesc('userid')->paginate(Base::getPaginate(50, 20));
//

View File

@ -33,6 +33,7 @@ use Carbon\Carbon;
* @property string|null $created_ip 注册IP
* @property string|null $disable_at 禁用时间(离职时间)
* @property int|null $email_verity 邮箱是否已验证
* @property int|null $bot 是否机器人
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Database\Factories\UserFactory factory(...$parameters)
@ -40,6 +41,7 @@ use Carbon\Carbon;
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|User query()
* @method static \Illuminate\Database\Eloquent\Builder|User whereAz($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereBot($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereChangepass($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedIp($value)
@ -77,7 +79,7 @@ class User extends AbstractModel
public static $defaultAvatarMode = 'auto';
// 基本信息的字段
public static $basicField = ['userid', 'email', 'nickname', 'profession', 'department', 'userimg', 'az', 'pinyin', 'line_at', 'disable_at'];
public static $basicField = ['userid', 'email', 'nickname', 'profession', 'department', 'userimg', 'bot', 'az', 'pinyin', 'line_at', 'disable_at'];
/**
* 更新数据校验
@ -109,17 +111,7 @@ class User extends AbstractModel
*/
public function getUserimgAttribute($value)
{
if ($value && !str_contains($value, 'avatar/')) {
// 自定义头像
return Base::fillUrl($value);
} else if (self::$defaultAvatarMode === 'auto') {
// 自动生成头像
return url("avatar/" . urlencode($this->nickname) . ".png");
} else {
// 系统默认头像
$name = ($this->userid - 1) % 21 + 1;
return url("images/avatar/default_{$name}.png");
}
return self::getAvatar($this->userid, $value, $this->email, $this->nickname);
}
/**
@ -171,7 +163,7 @@ class User extends AbstractModel
*/
public function getOnlineStatus()
{
$online = intval(Cache::get("User::online:" . $this->userid, 0));
$online = $this->bot || intval(Cache::get("User::online:" . $this->userid, 0)) > 0;
if ($online) {
return true;
}
@ -432,9 +424,12 @@ class User extends AbstractModel
if ($authInfo['userid'] > 0) {
$loginValid = floatval(Base::settingFind('system', 'loginValid')) ?: 720;
$loginValid *= 3600;
if ($authInfo['timestamp'] + $loginValid > time()) {
if ($authInfo['timestamp'] + $loginValid > time() || $authInfo['timestamp'] === -1) {
$row = self::whereUserid($authInfo['userid'])->whereEmail($authInfo['email'])->whereEncrypt($authInfo['encrypt'])->first();
if ($row) {
if (!$row->bot && $authInfo['timestamp'] === -1) {
return $_A["__static_auth"] = false; // 非机器人token时间不允许-1
}
$upArray = [];
if (Base::getIp() && $row->line_ip != Base::getIp()) {
$upArray['line_ip'] = Base::getIp();
@ -461,7 +456,8 @@ class User extends AbstractModel
*/
public static function token($userinfo)
{
$userinfo->token = base64_encode($userinfo->userid . '#$' . $userinfo->email . '#$' . $userinfo->encrypt . '#$' . time() . '#$' . Base::generatePassword(6));
$time = $userinfo->bot ? -1 : time();
$userinfo->token = base64_encode($userinfo->userid . '#$' . $userinfo->email . '#$' . $userinfo->encrypt . '#$' . $time . '#$' . Base::generatePassword(6));
unset($userinfo->encrypt);
unset($userinfo->password);
return $userinfo->token;
@ -538,6 +534,37 @@ class User extends AbstractModel
}
}
/**
* 获取头像
* @param $userid
* @param $userimg
* @param $email
* @param $nickname
* @return string
*/
public static function getAvatar($userid, $userimg, $email, $nickname)
{
// 自定义头像
if ($userimg && !str_contains($userimg, 'avatar/')) {
return Base::fillUrl($userimg);
}
// 机器人头像
if ($email == 'system-msg@bot.system') {
return url("images/avatar/default_system.png");
} elseif ($email == 'task-alert@bot.system') {
return url("images/avatar/default_task.png");
} elseif ($email == 'bot-manager@bot.system') {
return url("images/avatar/default_bot.png");
}
// 生成文字头像
if (self::$defaultAvatarMode === 'auto') {
return url("avatar/" . urlencode($nickname) . ".png");
}
// 系统默认头像
$name = ($userid - 1) % 21 + 1;
return url("images/avatar/default_{$name}.png");
}
/**
* 检测密码策略是否符合
* @param $password
@ -568,4 +595,51 @@ class User extends AbstractModel
}
}
}
/**
* 获取机器人或创建
* @param $key
* @param $update
* @param $userid
* @return self
*/
public static function botGetOrCreate($key, $update = [], $userid = 0)
{
$email = "{$key}@bot.system";
$botUser = self::whereEmail($email)->first();
if (empty($botUser)) {
$encrypt = Base::generatePassword(6);
$botUser = self::createInstance([
'bot' => 1,
'encrypt' => $encrypt,
'email' => $email,
'password' => Base::md52(Base::generatePassword(32), $encrypt),
'created_ip' => Base::getIp(),
]);
$botUser->save();
if ($userid > 0) {
UserBot::createInstance([
'userid' => $userid,
'bot_id' => $botUser->userid,
])->save();
}
//
if ($key === 'system-msg') {
$update['nickname'] = '系统消息';
} elseif ($key === 'task-alert') {
$update['nickname'] = '任务提醒';
} elseif ($key === 'bot-manager') {
$update['nickname'] = '机器人管理';
}
}
if ($update) {
$botUser->updateInstance($update);
if (isset($update['nickname'])) {
$botUser->az = Base::getFirstCharter($botUser->nickname);
$botUser->pinyin = Base::cn2pinyin($botUser->nickname);
}
$botUser->save();
}
return $botUser;
}
}

26
app/Models/UserBot.php Normal file
View File

@ -0,0 +1,26 @@
<?php
namespace App\Models;
/**
* App\Models\UserBot
*
* @property int $id
* @property int|null $userid 所属人ID
* @property int|null $bot_id 机器人ID
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|UserBot newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserBot newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserBot query()
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereBotId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserBot whereUserid($value)
* @mixin \Eloquent
*/
class UserBot extends AbstractModel
{
}

View File

@ -45,14 +45,7 @@ class UserDelete extends AbstractModel
$value['nickname'] = Base::cardFormat($value['email']);
}
// 头像
if ($value['userimg'] && !str_contains($value['userimg'], 'avatar/')) {
$value['userimg'] = Base::fillUrl($value['userimg']);
} else if (User::$defaultAvatarMode === 'auto') {
$value['userimg'] = url("avatar/" . urlencode($value['nickname']) . ".png");
} else {
$name = ($value['userid'] - 1) % 21 + 1;
$value['userimg'] = url("images/avatar/default_{$name}.png");
}
$value['userimg'] = User::getAvatar($value['userid'], $value['userimg'], $value['email'], $value['nickname']);
// 部门
$value['department'] = array_filter(is_array($value['department']) ? $value['department'] : Base::explodeInt($value['department']));
}

View File

@ -87,6 +87,7 @@ class WebSocketDialog extends AbstractModel
$this->dialog_user = null;
$this->group_info = null;
$this->top_at = $this->top_at ?? WebSocketDialogUser::whereDialogId($this->id)->whereUserid($userid)->value('top_at');
$this->bot = 0;
switch ($this->type) {
case "user":
$dialog_user = WebSocketDialogUser::whereDialogId($this->id)->where('userid', '!=', $userid)->first();
@ -96,6 +97,7 @@ class WebSocketDialog extends AbstractModel
$basic = User::userid2basic($dialog_user->userid);
if ($basic) {
$this->name = $basic->nickname;
$this->bot = $basic->bot;
} else {
$this->name = 'non-existent';
$this->dialog_delete = 1;

View File

@ -11,7 +11,7 @@ namespace App\Models;
* @property string|null $top_at 置顶时间
* @property int|null $mark_unread 是否标记为未读0否1是
* @property int|null $inviter 邀请人
* @property int|null $important 是否不可移出(项目、任务人员)
* @property int|null $important 是否不可移出(项目、任务、部门人员)
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newModelQuery()

View File

@ -1838,7 +1838,7 @@ class Base
$onlineip = '0,0,0,0';
}
preg_match("/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/", $onlineip, $match);
$_A["__static_ip"] = $match[0] ?: 'unknown';
$_A["__static_ip"] = $match ? ($match[0] ?: 'unknown') : '';
}
return $_A["__static_ip"];
}

View File

@ -5,16 +5,15 @@ namespace App\Tasks;
use App\Models\ProjectTask;
use App\Models\ProjectTaskPushLog;
use App\Models\User;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
use App\Module\Base;
use Carbon\Carbon;
use Hhxsv5\LaravelS\Swoole\Task\Task;
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
class AppPushTask extends AbstractTask
{
protected $endArray = [];
public function __construct()
{
parent::__construct();
@ -73,9 +72,7 @@ class AppPushTask extends AbstractTask
public function end()
{
foreach ($this->endArray as $task) {
Task::deliver($task);
}
}
/**
@ -95,7 +92,17 @@ class AppPushTask extends AbstractTask
return;
}
$botUser = User::botGetOrCreate('task-alert');
if (empty($botUser)) {
return;
}
$setting = Base::setting('appPushSetting');
$text = view('push.task', [
'type' => str_replace([0, 1, 2], ['start', 'before', 'after'], $type),
'task' => $task,
'setting' => $setting,
])->render();
/** @var User $user */
foreach ($users as $user) {
@ -108,25 +115,12 @@ class AppPushTask extends AbstractTask
if ($pushLog) {
continue;
}
$title = match ($type) {
1 => "任务提醒",
2 => "任务过期提醒",
default => "任务开始提醒",
};
$body = view('push.task', [
'type' => str_replace([0, 1, 2], ['start', 'before', 'after'], $type),
'user' => $user,
'task' => $task,
'setting' => $setting,
])->render();
$this->endArray[] = new PushUmengMsg($data['userid'], [
'title' => $title,
'body' => $body,
'description' => "TID:{$data['task_id']}",
'seconds' => 3600,
'badge' => 1,
]);
ProjectTaskPushLog::createInstance($data)->save();
//
$dialog = WebSocketDialog::checkUserDialog($botUser->userid, $data['userid']);
if ($dialog) {
ProjectTaskPushLog::createInstance($data)->save();
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $botUser->userid); // todo 未能在任务end事件来发送任务
}
}
}
}

View File

@ -0,0 +1,250 @@
<?php
namespace App\Tasks;
use App\Models\User;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
use App\Module\Base;
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
/**
* 推送会话消息
* Class BotReceiveMsgTask
* @package App\Tasks
*/
class BotReceiveMsgTask extends AbstractTask
{
protected $userid;
protected $msgId;
public function __construct($userid, $msgId)
{
parent::__construct(...func_get_args());
$this->userid = $userid;
$this->msgId = $msgId;
}
public function start()
{
$botUser = User::whereUserid($this->userid)->whereBot(1)->first();
if (empty($botUser)) {
return;
}
$msg = WebSocketDialogMsg::find($this->msgId);
if (empty($msg)) {
return;
}
$msg->readSuccess($botUser->userid);
//
$dialog = WebSocketDialog::find($msg->dialog_id);
if (empty($dialog)) {
return;
}
if ($dialog->type !== 'user') {
return;
}
if ($botUser->email === 'bot-manager@bot.system') {
$this->botManagerReceive($msg);
}
}
public function end()
{
}
/**
* 机器人管理处理消息
* @param WebSocketDialogMsg $msg
* @return void
*/
private function botManagerReceive(WebSocketDialogMsg $msg)
{
if ($msg->type === 'text') {
$text = trim(strip_tags($msg->msg['text']));
if (empty($text)) {
return;
}
$array = Base::newTrim(explode(" ", "{$text} "));
$type = $array[0];
$data = [];
$notice = "";
switch ($type) {
/**
* 列表
*/
case '/list':
$data = User::select(['users.*'])
->join('user_bots', 'users.userid', '=', 'user_bots.bot_id')
->where('users.bot', 1)
->where('user_bots.userid', $msg->userid)
->take(50)
->orderByDesc('id')
->get();
if ($data->isEmpty()) {
$type = "notice";
$notice = "您没有创建机器人。";
}
break;
/**
* 创建
*/
case '/newbot':
if (User::select(['users.*'])
->join('user_bots', 'users.userid', '=', 'user_bots.bot_id')
->where('users.bot', 1)
->where('user_bots.userid', $msg->userid)
->count() >= 50) {
$type = "notice";
$notice = "超过最大创建数量。";
break;
}
if (strlen($array[1]) < 2 || strlen($array[1]) > 20) {
$type = "notice";
$notice = "机器人名称由2-20个字符组成。";
break;
}
$data = User::botGetOrCreate("user-" . Base::generatePassword(), [
'nickname' => $array[1]
], $msg->userid);
if (empty($data)) {
$type = "notice";
$notice = "创建失败。";
break;
}
$dialog = WebSocketDialog::checkUserDialog($data->userid, $msg->userid);
if ($dialog) {
$text = "你好,我是你的机器人:{$data->nickname}, 我的机器人ID是{$data->userid}";
WebSocketDialogMsg::sendMsg(null, $dialog->id, 'text', ['text' => $text], $data->userid); // todo 未能在任务end事件来发送任务
}
break;
/**
* 修改名字
*/
case '/setname':
if (strlen($array[2]) < 2 || strlen($array[2]) > 20) {
$type = "notice";
$notice = "机器人名称由2-20个字符组成。";
break;
}
$data = $this->botManagerOne($array[1], $msg->userid);
if ($data) {
$data->nickname = $array[2];
$data->az = Base::getFirstCharter($array[2]);
$data->pinyin = Base::cn2pinyin($array[2]);
$data->save();
} else {
$type = "notice";
$notice = "机器人不存在。";
}
break;
/**
* 删除
*/
case '/deletebot':
$data = $this->botManagerOne($array[1], $msg->userid);
if ($data) {
$data->deleteUser('delete bot');
} else {
$type = "notice";
$notice = "机器人不存在。";
}
break;
/**
* 获取Token
*/
case '/token':
$data = $this->botManagerOne($array[1], $msg->userid);
if ($data) {
User::token($data);
} else {
$type = "notice";
$notice = "机器人不存在。";
}
break;
/**
* 更新Token
*/
case '/revoke':
$data = $this->botManagerOne($array[1], $msg->userid);
if ($data) {
$data->encrypt = Base::generatePassword(6);
$data->password = Base::md52(Base::generatePassword(32), $data->encrypt);
$data->save();
} else {
$type = "notice";
$notice = "机器人不存在。";
}
break;
/**
* 会话搜索
*/
case '/dialog':
$data = $this->botManagerOne($array[1], $msg->userid);
if ($data) {
$list = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->where('web_socket_dialogs.name', 'LIKE', "%{$array[2]}%")
->where('u.userid', $data->userid)
->orderByDesc('u.top_at')
->orderByDesc('web_socket_dialogs.last_at')
->take(20)
->get();
if ($list->isEmpty()) {
$type = "notice";
$notice = "没有搜索到相关会话。";
} else {
$list->transform(function (WebSocketDialog $item) use ($data) {
return $item->formatData($data->userid);
});
$data->list = $list;
}
} else {
$type = "notice";
$notice = "机器人不存在。";
}
break;
}
//
$text = view('push.bot', [
'type' => $type,
'data' => $data,
'notice' => $notice,
'version' => Base::getVersion()
])->render();
$text = preg_replace("/^\x20+/", "", $text);
$text = preg_replace("/\n\x20+/", "\n", $text);
WebSocketDialogMsg::sendMsg(null, $msg->dialog_id, 'text', ['text' => $text], $this->userid); // todo 未能在任务end事件来发送任务
}
}
/**
* @param $botId
* @param $userid
* @return User
*/
private function botManagerOne($botId, $userid)
{
$botId = intval($botId);
$userid = intval($userid);
if ($botId > 0) {
return User::select(['users.*'])
->join('user_bots', 'users.userid', '=', 'user_bots.bot_id')
->where('users.bot', 1)
->where('user_bots.bot_id', $botId)
->where('user_bots.userid', $userid)
->first();
}
return null;
}
}

View File

@ -5,6 +5,7 @@ namespace App\Tasks;
@error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
use App\Models\User;
use App\Models\UserBot;
use App\Models\WebSocketDialog;
use App\Models\WebSocketDialogMsg;
use App\Models\WebSocketDialogMsgRead;
@ -120,6 +121,11 @@ class WebSocketDialogMsgTask extends AbstractTask
'mention' => $mention,
])->saveOrIgnore();
$array[$userid] = $mention;
// 机器人收到消处理
$botUser = User::whereUserid($userid)->whereBot(1)->first();
if ($botUser) {
$this->endArray[] = new BotReceiveMsgTask($botUser->userid, $msg->id);
}
}
}
// 更新已发送数量

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserBotsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (Schema::hasTable('user_bots'))
return;
Schema::create('user_bots', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('userid')->nullable()->default(0)->comment('所属人ID');
$table->bigInteger('bot_id')->nullable()->default(0)->comment('机器人ID');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_bots');
}
}

View File

@ -0,0 +1,40 @@
<?php
use App\Models\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddUsersBot extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$isAdd = false;
Schema::table('users', function (Blueprint $table) use (&$isAdd) {
if (!Schema::hasColumn('users', 'bot')) {
$isAdd = true;
$table->tinyInteger('bot')->nullable()->default(0)->after('email_verity')->comment('是否机器人');
}
});
if ($isAdd) {
User::botGetOrCreate('bot-manager');
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn("bot");
});
}
}

View File

@ -3,7 +3,7 @@
v-if="user"
class="common-avatar"
:open-delay="openDelay"
:disabled="windowSmall || tooltipDisabled"
:disabled="windowSmall || tooltipDisabled || isBot"
:placement="tooltipPlacement">
<div slot="content" class="common-avatar-transfer">
<slot/>
@ -27,9 +27,10 @@
<span class="avatar-char" :style="spotStyle">{{nickname}}</span>
</EAvatar>
</div>
<template v-if="showName">
<div class="avatar-name" :style="nameStyle">{{nameText || user.nickname}}</div>
</template>
<div v-if="showName" class="avatar-name" :style="nameStyle">
<div v-if="user.bot" class="taskfont bot">&#xe68c;</div>
<span>{{nameText || user.nickname}}</span>
</div>
</div>
</ETooltip>
</template>
@ -126,7 +127,7 @@
boxClass() {
return {
'avatar-box': true,
'online': this.userId === this.userid || this.user.online,
'online': this.userId === this.userid || this.user.online || this.isBot,
'disabled': this.user.disable_at,
'deleted': this.user.delete_at
}
@ -158,7 +159,7 @@
const {delete_at, disable_at} = this.user
const styles = {}
if (!showIcon) {
styles.paddingLeft = 0
styles.marginLeft = 0
}
if (delete_at || disable_at) {
styles.opacity = 0.8
@ -200,6 +201,10 @@
return $A.strExists(userimg, '/avatar');
},
isBot() {
return !!(this.user && this.user.bot);
},
nickname() {
const {nickname} = this.user;
if (!nickname) {

View File

@ -30,6 +30,7 @@
:disabled="isDisabled(item.userid)">
<div class="user-input-option">
<div class="user-input-avatar"><EAvatar class="avatar" :src="item.userimg"/></div>
<div v-if="item.bot" class="taskfont user-input-bot">&#xe68c;</div>
<div class="user-input-nickname">{{ item.nickname }}</div>
<div class="user-input-userid">ID: {{ item.userid }}</div>
</div>
@ -94,6 +95,10 @@
type: Number,
default: 0
},
showBot: {
type: Boolean,
default: false
},
},
data() {
return {
@ -175,6 +180,7 @@
project_id: this.projectId,
no_project_id: this.noProjectId,
dialog_id: this.dialogId,
bot: this.showBot ? 2 : 0,
},
take: 50
},

View File

@ -570,7 +570,8 @@ export default {
return `<div class="mention-item-at">@</div><div class="mention-item-name">${data.value}</div><div class="mention-item-tip">${data.tip}</div>`;
}
if (data.avatar) {
return `<div class="mention-item-img${data.online ? ' online' : ''}"><img src="${data.avatar}"/><em></em></div><div class="mention-item-name">${data.value}</div>`;
const botHtml = data.bot ? `<div class="taskfont mention-item-bot">&#xe68c;</div>` : ''
return `<div class="mention-item-img${data.online ? ' online' : ''}"><img src="${data.avatar}"/><em></em></div>${botHtml}<div class="mention-item-name">${data.value}</div>`;
}
return `<div class="mention-item-name" title="${data.value}">${data.value}</div>`;
},
@ -1115,6 +1116,7 @@ export default {
value: item.nickname,
avatar: item.userimg,
online: item.online,
bot: item.bot,
}
}))
}
@ -1134,6 +1136,7 @@ export default {
value: item.nickname,
avatar: item.userimg,
online: item.online,
bot: item.bot,
})
}
})

View File

@ -43,7 +43,7 @@
:mask-closable="false">
<Form :model="addData" label-width="auto" @submit.native.prevent>
<FormItem prop="userids" :label="$L('新增成员')">
<UserInput v-model="addData.userids" :disabledChoice="addData.disabledChoice" :multiple-max="100" :placeholder="$L('选择成员')"/>
<UserInput v-model="addData.userids" :disabledChoice="addData.disabledChoice" :multiple-max="100" show-bot :placeholder="$L('选择成员')"/>
<div v-if="dialogData.group_type === 'department'" class="form-tip">{{$L('此操作仅加入群成员并不会加入部门')}}</div>
<div v-else-if="dialogData.group_type === 'project'" class="form-tip">{{$L('此操作仅加入群成员并不会加入项目')}}</div>
<div v-else-if="dialogData.group_type === 'task'" class="form-tip">{{$L('此操作仅加入群成员并不会加入任务负责人')}}</div>

View File

@ -41,6 +41,7 @@
</template>
<h2>{{dialogData.name}}</h2>
<em v-if="peopleNum > 0">({{peopleNum}})</em>
<Tag v-if="dialogData.bot" class="after" :fade="false">{{$L('机器人')}}</Tag>
<Tag v-if="dialogData.group_type=='all'" class="after" :fade="false">{{$L('全员')}}</Tag>
<Tag v-else-if="dialogData.group_type=='department'" class="after" :fade="false">{{$L('部门')}}</Tag>
</div>
@ -276,7 +277,7 @@
:mask-closable="false">
<Form :model="createGroupData" label-width="auto" @submit.native.prevent>
<FormItem prop="userids" :label="$L('群成员')">
<UserInput v-model="createGroupData.userids" :uncancelable="createGroupData.uncancelable" :multiple-max="100" :placeholder="$L('选择项目成员')"/>
<UserInput v-model="createGroupData.userids" :uncancelable="createGroupData.uncancelable" :multiple-max="100" show-bot :placeholder="$L('选择项目成员')"/>
</FormItem>
<FormItem prop="chat_name" :label="$L('群名称')">
<Input v-model="createGroupData.chat_name" :placeholder="$L('输入群名称(选填)')"/>
@ -2222,7 +2223,7 @@ export default {
};
img.src = url;
})
}
},
}
}
</script>

View File

@ -66,6 +66,7 @@
<div class="dialog-title">
<div v-if="dialog.todo_num" class="todo">[{{$L('待办')}}{{formatTodoNum(dialog.todo_num)}}]</div>
<div v-if="$A.getDialogMention(dialog) > 0" class="mention">[@{{$A.getDialogMention(dialog)}}]</div>
<div v-if="dialog.bot" class="taskfont bot">&#xe68c;</div>
<template v-for="tag in $A.dialogTags(dialog)" v-if="tag.color != 'success'">
<Tag :color="tag.color" :fade="false" @on-click="openDialog(dialog.id)">{{$L(tag.text)}}</Tag>
</template>
@ -263,7 +264,7 @@ export default {
}
break;
case 'user':
if (dialog.type != 'user') {
if (dialog.type != 'user' || dialog.bot) {
return false;
}
break;

View File

@ -192,8 +192,10 @@
&.avatar-wrapper {
align-items: flex-start;
.avatar-name {
font-weight: bold;
padding-left: 12px;
margin-left: 12px;
> span {
font-weight: bold;
}
}
}
}

View File

@ -67,11 +67,29 @@
}
}
}
.avatar-name {
.avatar-bot {
margin-right: -4px;
padding-left: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 16px;
color: $primary-color;
}
.avatar-name {
display: flex;
align-items: center;
margin-left: 6px;
.bot {
flex-shrink: 0;
margin-right: 3px;
font-size: 16px;
color: $primary-color;
}
> span {
flex: 1;
font-style: normal;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
&.avatar-pointer {

View File

@ -35,6 +35,12 @@
line-height: 26px;
}
}
.user-input-bot {
font-size: 16px;
margin-left: 10px;
margin-right: -6px;
color: $primary-color;
}
.user-input-nickname {
margin-left: 10px;
flex: 1;

View File

@ -665,6 +665,13 @@
}
}
.mention-item-bot {
font-size: 16px;
margin-left: 8px;
margin-right: -5px;
color: $primary-color;
}
.mention-item-name {
padding: 0 8px;
font-size: 14px;

View File

@ -96,7 +96,7 @@
flex: 1;
.avatar-name {
padding-left: 8px;
margin-left: 8px;
}
}

View File

@ -52,7 +52,7 @@
width: 0;
flex: 1;
.avatar-name {
padding-left: 8px;
margin-left: 8px;
}
}
}

View File

@ -1274,7 +1274,7 @@
.common-avatar {
width: 100%;
.avatar-name {
padding-right: 6px;
margin-left: 6px;
}
}

View File

@ -654,7 +654,7 @@
line-height: 32px;
}
.avatar-name {
padding-left: 8px;
margin-left: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;

View File

@ -150,7 +150,8 @@
justify-content: space-between;
line-height: 24px;
.todo,
.mention {
.mention,
.bot {
color: #ff0000;
background-color: transparent;
font-weight: 600;
@ -161,6 +162,11 @@
width: auto;
user-select: none;
}
.bot {
color: $primary-color;
font-size: 16px;
font-weight: normal;
}
.ivu-tag {
margin: 0 4px 0 0;
padding: 0 5px;

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -4,5 +4,5 @@
@elseif ($type === 'after')
<p>您的任务【{{ $task->name }}】已经超时{{ $setting['task_remind_hours2'] > 0 ? "{$setting['task_remind_hours2']}小时" : "" }},请及时处理。</p>
@else
<p>您有一个新任务【{{ $task->name }}】已开始,请及时处理</p>
<p>您有一个新任务【{{ $task->name }}】已开始</p>
@endif

View File

@ -0,0 +1,73 @@
@if ($type === '/help')
您可以通过发送以下命令来控制我:
<span style="color:#84c56a">/list</span> - 机器人列表
<span style="color:#84c56a">/newbot {机器人名称}</span> - 创建机器人
<b>修改机器人</b>
<span style="color:#84c56a">/setname {机器人ID} {机器人名称}</span> - 修改机器人名称
<span style="color:#84c56a">/deletebot {机器人ID}</span> - 删除机器人
<b>机器人设置</b>
<span style="color:#84c56a">/token {机器人ID}</span> - 生成Token令牌
<span style="color:#84c56a">/revoke {机器人ID}</span> - 撤销机器人Token令牌
<b>会话管理</b>
<span style="color:#84c56a">/dialog {机器人ID} [搜索关键词]</span> - 查看会话ID
<b>Api接口文档</b>
<span style="color:#84c56a">/api</span> - 查看接口列表
@elseif ($type === '/list')
<b>我的机器人。</b>
<b>机器人ID | 机器人名称</b>
@foreach($data as $item)
{{$item->userid}} | {{$item->nickname}}
@endforeach
@elseif ($type === '/newbot')
<b>创建成功。</b>
机器人ID<span style="color:#84c56a">{{$data->userid}}</span>
机器人名称:<span style="color:#84c56a">{{$data->nickname}}</span>
@elseif ($type === '/setname')
<b>设置名称成功。</b>
机器人ID<span style="color:#84c56a">{{$data->userid}}</span>
机器人名称:<span style="color:#84c56a">{{$data->nickname}}</span>
@elseif ($type === '/deletebot')
<b>删除成功。</b>
机器人ID<span style="color:#84c56a">{{$data->userid}}</span>
机器人名称:<span style="color:#84c56a">{{$data->nickname}}</span>
@elseif ($type === '/token')
<b>生成Token令牌。</b>
机器人ID<span style="color:#84c56a">{{$data->userid}}</span>
机器人名称:<span style="color:#84c56a">{{$data->nickname}}</span>
Token<span style="color:#84c56a">{{$data->token}}</span>
@elseif ($type === '/revoke')
<b>撤销机器人Token令牌。</b>
机器人ID<span style="color:#84c56a">{{$data->userid}}</span>
机器人名称:<span style="color:#84c56a">{{$data->nickname}}</span>
@elseif ($type === '/dialog')
<b>机器人 <span style="color:#84c56a">{{$data->nickname}} (ID:{{$data->userid}})</span> 已加入的会话:</b>
<b>会话ID | 会话名称</b>
@foreach($data->list as $item)
{{$item->id}} | {{$item->name}}{{$item->type == 'user' ? ' (个人)' : ''}}
@endforeach
@elseif ($type === '/api')
你可以通过执行以下命令来请求我:
<b>发送文本消息</b>
curl --request POST '{{url('api/dialog/msg/sendtext')}}' \
--header 'version: {{ $version }}' \
--header 'token: <span style="color:#84c56a">{机器人Token}</span>' \
--form 'dialog_id="<span style="color:#84c56a">{对话ID}</span>"' \
--form 'text="<span style="color:#84c56a">{消息内容}</span>"'
@elseif ($type === 'notice')
{{$notice}}
@else
你好,我是你的机器人助理,你可以发送 <span style="color:#84c56a">/help</span> 查看帮助菜单。
@endif

View File

@ -1,7 +1,7 @@
@if ($type === 'before')
您有一个任务{{ $task->name }}{{ $setting['task_remind_hours'] > 0 ? "还有{$setting['task_remind_hours']}小时即将超时" : "已超时" }},请及时处理。
您有一个任务 <span class="mention task" data-id="{{ $task->id }}">#{{ $task->name }}</span> {{ $setting['task_remind_hours'] > 0 ? "还有{$setting['task_remind_hours']}小时即将超时" : "已超时" }},请及时处理。
@elseif ($type === 'after')
您的任务{{ $task->name }}】已经超时{{ $setting['task_remind_hours2'] > 0 ? "{$setting['task_remind_hours2']}小时" : "" }},请及时处理。
您的任务 <span class="mention task" data-id="{{ $task->id }}">#{{ $task->name }}</span> 已经超时{{ $setting['task_remind_hours2'] > 0 ? "{$setting['task_remind_hours2']}小时" : "" }},请及时处理。
@else
您有一个新任务{{ $task->name }}】已开始,请及时处理。
您有一个新任务 <span class="mention task" data-id="{{ $task->id }}">#{{ $task->name }}</span> 已开始。
@endif