@juzi/wechaty
Version:
Wechaty is a RPA SDK for Chatbot Makers.
312 lines • 11 kB
JavaScript
/**
* Wechaty Chatbot SDK - https://github.com/wechaty/wechaty
*
* @copyright 2016 Huan LI (李卓桓) <https://github.com/huan>, and
* Wechaty Contributors <https://github.com/wechaty>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import * as PUPPET from '@juzi/wechaty-puppet';
import { concurrencyExecuter } from 'rx-queue';
import { log } from '../config.js';
import { poolifyMixin } from '../user-mixins/poolify.js';
import assert from 'assert';
import { validationMixin } from '../user-mixins/validation.js';
import { wechatifyMixin, } from '../user-mixins/wechatify.js';
const MixinBase = wechatifyMixin(poolifyMixin(Object)());
class TagMixin extends MixinBase {
id;
/**
*
* Instance properties
* @ignore
*
*/
payload;
/**
* @hideconstructor
*/
constructor(id) {
super();
this.id = id;
log.silly('Tag', 'constructor()');
}
type() {
return (this.payload && this.payload.type) || PUPPET.types.Tag.Unspecific;
}
name() {
return (this.payload && this.payload.name) || '';
}
groupId() {
return (this.payload && this.payload.groupId) || '';
}
async group() {
return this.payload?.groupId ? this.wechaty.TagGroup.find({ id: this.payload.groupId }) : undefined;
}
static async list() {
log.verbose('Tag', 'list()');
const tagIdList = await this.wechaty.puppet.tagTagList();
let continuousErrorCount = 0;
let totalErrorCount = 0;
const totalErrorThreshold = Math.round(tagIdList.length / 5);
const idToTag = async (id) => {
if (!this.wechaty.isLoggedIn) {
throw new Error('wechaty not logged in');
}
const result = this.find({ id }).catch(e => {
this.wechaty.emitError(e);
continuousErrorCount++;
totalErrorCount++;
if (continuousErrorCount > 5) {
throw new Error('5 continuous errors!');
}
if (totalErrorCount > totalErrorThreshold) {
throw new Error(`${totalErrorThreshold} total errors!`);
}
});
continuousErrorCount = 0;
return result;
};
const CONCURRENCY = 17;
const tagIterator = concurrencyExecuter(CONCURRENCY)(idToTag)(tagIdList);
const tagList = [];
for await (const tag of tagIterator) {
if (tag) {
tagList.push(tag);
}
}
return tagList;
}
static async find(filter) {
log.silly('Tag', 'find(%s)', JSON.stringify(filter));
if (filter.id) {
const tag = this.wechaty.Tag.load(filter.id);
try {
await tag.ready();
}
catch (e) {
this.wechaty.emitError(e);
return undefined;
}
return tag;
}
if (filter.name) {
const tags = (await this.wechaty.Tag.list()).filter(t => t.name() === filter.name);
if (tags.length > 0) {
return tags[0];
}
}
return undefined;
// TODO: use a puppet method to find tag, like how contact and room do it
}
static async findMulti(filters) {
log.silly('Tag', 'find(%s)', JSON.stringify(filters));
const filterIdList = filters.filter(i => !!i.id).map(i => i.id);
const filterNameList = filters.filter(i => !!i.name).map(i => i.name);
const resultSet = new Set();
if (filterIdList.length) {
const tags = filterIdList.map(i => {
assert(i, 'Should not be undefined');
return this.wechaty.Tag.load(i);
});
try {
await Promise.all(tags.map(i => i.ready()));
}
catch (e) {
this.wechaty.emitError(e);
return undefined;
}
for (const tag of tags) {
resultSet.add(tag);
}
}
if (filterNameList.length) {
const list = await this.wechaty.Tag.list();
const tagMap = new Map();
// FIXME: There are two tag types in wework, and the names can be repeated
list.forEach(i => tagMap.set(i.name(), i));
for (const name of filterNameList) {
assert(typeof name === 'string', 'Only supported string');
const tag = tagMap.get(name);
if (tag) {
resultSet.add(tag);
}
}
}
return resultSet.size ? Array.from(resultSet) : undefined;
// TODO: use a puppet method to find tag, like how contact and room do it
}
/**
* Force reload data for Tag, Sync data from low-level API again.
*
* @returns {Promise<this>}
* @example
* await tag.sync()
*/
async sync() {
await this.wechaty.puppet.tagPayloadDirty(this.id);
await this.ready(true);
}
/**
* @ignore
*/
isReady() {
return !!(this.payload && this.payload.name);
}
/**
* `ready()` is For FrameWork ONLY!
*
* Please not to use `ready()` at the user land.
* If you want to sync data, use `sync()` instead.
*
* @ignore
*/
async ready(forceSync = false) {
log.silly('Tag', 'ready() @ %s with Tag key="%s"', this.wechaty.puppet, this.id);
if (!forceSync && this.isReady()) { // already ready
log.silly('Tag', 'ready() isReady() true');
return;
}
try {
this.payload = await this.wechaty.puppet.tagPayload(this.id);
}
catch (e) {
this.wechaty.emitError(e);
log.verbose('Tag', 'ready() this.wechaty.puppet.tagPayload(%s) exception: %s', this.id, e.message);
throw e;
}
}
async contactList() {
log.verbose('Tag', 'contactList() for tag : %s', this);
const contactIds = await this.wechaty.puppet.tagTagContactList(this.id);
const contactPromises = contactIds.map(id => this.wechaty.Contact.find({ id }));
return (await Promise.all(contactPromises)).filter(contact => !!contact);
}
async tag(contacts) {
log.verbose('Tag', 'tag(%s) for tag : %s', contacts, this);
let contactIds;
if (Array.isArray(contacts)) {
contactIds = contacts.map(c => c.id);
}
else {
contactIds = [contacts.id];
}
await this.wechaty.puppet.tagContactTagAdd([this.id], contactIds);
}
static async createTag(name, tagGroup) {
log.verbose('Tag', 'createTag(%s, %s)', tagGroup, name);
try {
const tagInfoList = await this.wechaty.puppet.tagTagAdd([name], tagGroup?.name());
if (tagInfoList?.length) {
const filter = {
id: tagInfoList[0]?.id,
};
const newTag = await this.find(filter);
return newTag;
}
}
catch (e) {
this.wechaty.emitError(e);
log.error('Tag', 'createTag() exception: %s', e.message);
}
}
static async createMultiTag(nameList, tagGroup) {
log.verbose('Tag', 'createMultiTag(%s, %s)', tagGroup, nameList);
try {
const tagInfoList = await this.wechaty.puppet.tagTagAdd(nameList, tagGroup?.name());
if (tagInfoList?.length) {
const filterList = tagInfoList.map(i => ({
id: i.id,
}));
const newTagList = await this.findMulti(filterList);
return newTagList;
}
}
catch (e) {
this.wechaty.emitError(e);
log.error('Tag', 'createMultiTag() exception: %s', e.message);
}
}
static async deleteTag(tagInstance) {
log.verbose('Tag', 'deleteTag(%s, %s)', tagInstance);
try {
await this.wechaty.puppet.tagTagDelete([tagInstance.id]);
}
catch (e) {
this.wechaty.emitError(e);
log.error('Tag', 'deleteTag() exception: %s', e.message);
}
}
static async deleteMultiTag(tagInstances) {
log.verbose('Tag', 'deleteMultiTag(%s, %s)', tagInstances);
try {
await this.wechaty.puppet.tagTagDelete(tagInstances.map(i => i.id));
}
catch (e) {
this.wechaty.emitError(e);
log.error('Tag', 'deleteMultiTag() exception: %s', e.message);
}
}
static async modifyTag(tagInstance, tagNewName) {
log.verbose('Tag', 'modifyTag(%s, %s)', tagInstance);
try {
const tagNewInfo = {
id: tagInstance.id,
name: tagNewName,
};
const tagInfoList = await this.wechaty.puppet.tagTagModify([tagNewInfo]);
if (tagInfoList?.length) {
const filter = {
id: tagInfoList[0]?.id,
};
const newTag = await this.find(filter);
return newTag;
}
}
catch (e) {
this.wechaty.emitError(e);
log.error('Tag', 'modifyTag() exception: %s', e.message);
}
}
static async modifyMultiTag(tagInfos) {
log.verbose('Tag', 'modifyMultiTag(%o)', tagInfos);
try {
const tagNewInfoList = tagInfos.map(i => ({
id: i.tag.id,
name: i.newName,
}));
const tagInfoList = await this.wechaty.puppet.tagTagModify(tagNewInfoList);
if (tagInfoList?.length) {
const filterList = tagInfoList.map(i => ({
id: i.id,
}));
const newTagList = await this.findMulti(filterList);
return newTagList;
}
}
catch (e) {
this.wechaty.emitError(e);
log.error('Tag', 'modifyMultiTag() exception: %s', e.message);
}
}
toString() {
return `<Tag#${this.name() || this.id}>`;
}
}
class TagImplBase extends validationMixin(TagMixin)() {
}
class TagImpl extends validationMixin(TagImplBase)() {
}
export { TagImpl, };
//# sourceMappingURL=tag.js.map