mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-18 11:22:21 -06:00
feat: Added group invitation sending and Added webhook configuration per event in the individual instance registration
This commit is contained in:
@@ -87,7 +87,6 @@ export type Instance = {
|
||||
NAME: string;
|
||||
WEBHOOK_URL: string;
|
||||
MODE: string;
|
||||
WEBHOOK_BY_EVENTS: boolean;
|
||||
};
|
||||
export type Auth = {
|
||||
API_KEY: ApiKey;
|
||||
@@ -263,8 +262,6 @@ export class ConfigService {
|
||||
NAME: process.env.AUTHENTICATION_INSTANCE_NAME,
|
||||
WEBHOOK_URL: process.env.AUTHENTICATION_INSTANCE_WEBHOOK_URL,
|
||||
MODE: process.env.AUTHENTICATION_INSTANCE_MODE,
|
||||
WEBHOOK_BY_EVENTS:
|
||||
process.env.AUTHENTICATION_INSTANCE_WEBHOOK_BY_EVENTS === 'true',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ LOG:
|
||||
# Determine how long the instance should be deleted from memory in case of no connection.
|
||||
# Default time: 5 minutes
|
||||
# If you don't even want an expiration, enter the value false
|
||||
DEL_INSTANCE: false # or false
|
||||
DEL_INSTANCE: 5 # or false
|
||||
|
||||
# Temporary data storage
|
||||
STORE:
|
||||
@@ -112,7 +112,7 @@ WEBHOOK:
|
||||
CONFIG_SESSION_PHONE:
|
||||
# Name that will be displayed on smartphone connection
|
||||
CLIENT: 'Evolution API'
|
||||
NAME: Chrome # firefox | edge | opera | safari
|
||||
NAME: chrome # chrome | firefox | edge | opera | safari
|
||||
|
||||
# Set qrcode display limit
|
||||
QRCODE:
|
||||
@@ -120,11 +120,11 @@ QRCODE:
|
||||
|
||||
# Defines an authentication type for the api
|
||||
AUTHENTICATION:
|
||||
TYPE: apikey # or jwt apikey
|
||||
TYPE: apikey # jwt or apikey
|
||||
# Define a global apikey to access all instances
|
||||
API_KEY:
|
||||
# OBS: This key must be inserted in the request header to create an instance.
|
||||
KEY: B6D711FC-DE4D-4FD5-9365-44120E713976
|
||||
KEY: B6D711FCDE4D4FD5936544120E713976
|
||||
# Expose the api key on return from fetch instances
|
||||
EXPOSE_IN_FETCH_INSTANCES: true
|
||||
# Set the secret key to encrypt and decrypt your token and its expiration time.
|
||||
@@ -134,7 +134,6 @@ AUTHENTICATION:
|
||||
# Set the instance name and webhook url to create an instance in init the application
|
||||
INSTANCE:
|
||||
# With this option activated, you work with a url per webhook event, respecting the local url and the name of each event
|
||||
WEBHOOK_BY_EVENTS: false
|
||||
MODE: server # container or server
|
||||
# if you are using container mode, set the container name and the webhook url to default instance
|
||||
NAME: evolution
|
||||
|
||||
@@ -27,6 +27,7 @@ export const instanceNameSchema: JSONSchema7 = {
|
||||
properties: {
|
||||
instanceName: { type: 'string' },
|
||||
webhook: { type: 'string' },
|
||||
webhook_by_events: { type: 'boolean' },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
@@ -665,6 +666,28 @@ export const groupJidSchema: JSONSchema7 = {
|
||||
...isNotEmpty('groupJid'),
|
||||
};
|
||||
|
||||
export const groupSendInviteSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
groupJid: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
numbers: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
uniqueItems: true,
|
||||
items: {
|
||||
type: 'string',
|
||||
minLength: 10,
|
||||
pattern: '\\d+',
|
||||
description: '"numbers" must be an array of numeric strings',
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['groupJid', 'description', 'numbers'],
|
||||
...isNotEmpty('groupJid', 'description', 'numbers'),
|
||||
};
|
||||
|
||||
export const groupInviteSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
GroupInvite,
|
||||
GroupJid,
|
||||
GroupPictureDto,
|
||||
GroupSendInvite,
|
||||
GroupSubjectDto,
|
||||
GroupToggleEphemeralDto,
|
||||
GroupUpdateParticipantDto,
|
||||
@@ -56,10 +57,8 @@ export class GroupController {
|
||||
return await this.waMonitor.waInstances[instance.instanceName].inviteInfo(inviteCode);
|
||||
}
|
||||
|
||||
public async acceptInvite(instance: InstanceDto, inviteCode: GroupInvite) {
|
||||
return await this.waMonitor.waInstances[instance.instanceName].acceptInvite(
|
||||
inviteCode,
|
||||
);
|
||||
public async sendInvite(instance: InstanceDto, data: GroupSendInvite) {
|
||||
return await this.waMonitor.waInstances[instance.instanceName].sendInvite(data);
|
||||
}
|
||||
|
||||
public async revokeInviteCode(instance: InstanceDto, groupJid: GroupJid) {
|
||||
|
||||
@@ -26,6 +26,7 @@ export class InstanceController {
|
||||
public async createInstance({
|
||||
instanceName,
|
||||
webhook,
|
||||
webhook_by_events,
|
||||
events,
|
||||
qrcode,
|
||||
token,
|
||||
@@ -60,7 +61,12 @@ export class InstanceController {
|
||||
|
||||
if (webhook) {
|
||||
try {
|
||||
this.webhookService.create(instance, { enabled: true, url: webhook, events });
|
||||
this.webhookService.create(instance, {
|
||||
enabled: true,
|
||||
url: webhook,
|
||||
events,
|
||||
webhook_by_events,
|
||||
});
|
||||
|
||||
getEvents = (await this.webhookService.find(instance)).events;
|
||||
} catch (error) {
|
||||
@@ -98,7 +104,12 @@ export class InstanceController {
|
||||
|
||||
if (webhook) {
|
||||
try {
|
||||
this.webhookService.create(instance, { enabled: true, url: webhook, events });
|
||||
this.webhookService.create(instance, {
|
||||
enabled: true,
|
||||
url: webhook,
|
||||
events,
|
||||
webhook_by_events,
|
||||
});
|
||||
|
||||
getEvents = (await this.webhookService.find(instance)).events;
|
||||
} catch (error) {
|
||||
@@ -121,6 +132,7 @@ export class InstanceController {
|
||||
},
|
||||
hash,
|
||||
webhook,
|
||||
webhook_by_events,
|
||||
events: getEvents,
|
||||
qrcode: getQrcode,
|
||||
};
|
||||
|
||||
@@ -27,6 +27,12 @@ export class GroupInvite {
|
||||
inviteCode: string;
|
||||
}
|
||||
|
||||
export class GroupSendInvite {
|
||||
groupJid: string;
|
||||
description: string;
|
||||
numbers: string[];
|
||||
}
|
||||
|
||||
export class GroupUpdateParticipantDto extends GroupJid {
|
||||
action: 'add' | 'remove' | 'promote' | 'demote';
|
||||
participants: string[];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export class InstanceDto {
|
||||
instanceName: string;
|
||||
webhook?: string;
|
||||
webhook_by_events?: boolean;
|
||||
events?: string[];
|
||||
qrcode?: boolean;
|
||||
token?: string;
|
||||
|
||||
@@ -2,4 +2,5 @@ export class WebhookDto {
|
||||
enabled?: boolean;
|
||||
url?: string;
|
||||
events?: string[];
|
||||
webhook_by_events?: boolean;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export class WebhookRaw {
|
||||
url?: string;
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
webhook_by_events?: boolean;
|
||||
}
|
||||
|
||||
const webhookSchema = new Schema<WebhookRaw>({
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
updateGroupSubjectSchema,
|
||||
updateGroupDescriptionSchema,
|
||||
groupInviteSchema,
|
||||
groupSendInviteSchema,
|
||||
} from '../../validate/validate.schema';
|
||||
import { RouterBroker } from '../abstract/abstract.router';
|
||||
import {
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
GroupUpdateParticipantDto,
|
||||
GroupUpdateSettingDto,
|
||||
GroupToggleEphemeralDto,
|
||||
GroupSendInvite,
|
||||
} from '../dto/group.dto';
|
||||
import { groupController } from '../whatsapp.module';
|
||||
import { HttpStatus } from './index.router';
|
||||
@@ -120,12 +122,12 @@ export class GroupRouter extends RouterBroker {
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
})
|
||||
.get(this.routerPath('acceptInvite'), ...guards, async (req, res) => {
|
||||
const response = await this.inviteCodeValidate<GroupInvite>({
|
||||
.post(this.routerPath('sendInvite'), ...guards, async (req, res) => {
|
||||
const response = await this.groupNoValidate<GroupSendInvite>({
|
||||
request: req,
|
||||
schema: groupInviteSchema,
|
||||
ClassRef: GroupInvite,
|
||||
execute: (instance, data) => groupController.acceptInvite(instance, data),
|
||||
schema: groupSendInviteSchema,
|
||||
ClassRef: GroupSendInvite,
|
||||
execute: (instance, data) => groupController.sendInvite(instance, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
|
||||
@@ -104,6 +104,7 @@ import {
|
||||
GroupToggleEphemeralDto,
|
||||
GroupSubjectDto,
|
||||
GroupDescriptionDto,
|
||||
GroupSendInvite,
|
||||
} from '../dto/group.dto';
|
||||
import { MessageUpQuery } from '../repository/messageUp.repository';
|
||||
import { useMultiFileAuthStateDb } from '../../utils/use-multi-file-auth-state-db';
|
||||
@@ -204,6 +205,7 @@ export class WAStartupService {
|
||||
this.localWebhook.url = data?.url;
|
||||
this.localWebhook.enabled = data?.enabled;
|
||||
this.localWebhook.events = data?.events;
|
||||
this.localWebhook.webhook_by_events = data?.webhook_by_events;
|
||||
}
|
||||
|
||||
public async setWebhook(data: WebhookRaw) {
|
||||
@@ -224,11 +226,9 @@ export class WAStartupService {
|
||||
|
||||
if (Array.isArray(webhookLocal) && webhookLocal.includes(we)) {
|
||||
if (local && instance.MODE !== 'container') {
|
||||
const { WEBHOOK_BY_EVENTS } = instance;
|
||||
|
||||
let baseURL;
|
||||
|
||||
if (WEBHOOK_BY_EVENTS) {
|
||||
if (this.localWebhook.webhook_by_events) {
|
||||
baseURL = `${this.localWebhook.url}/${transformedWe}`;
|
||||
} else {
|
||||
baseURL = this.localWebhook.url;
|
||||
@@ -484,7 +484,7 @@ export class WAStartupService {
|
||||
|
||||
const { version } = await fetchLatestBaileysVersion();
|
||||
const session = this.configService.get<ConfigSessionPhone>('CONFIG_SESSION_PHONE');
|
||||
const browser: WABrowserDescription = [session.CLIENT, 'Chrome', release()];
|
||||
const browser: WABrowserDescription = [session.CLIENT, session.NAME, release()];
|
||||
|
||||
const socketConfig: UserFacingSocketConfig = {
|
||||
auth: {
|
||||
@@ -1773,11 +1773,28 @@ export class WAStartupService {
|
||||
}
|
||||
}
|
||||
|
||||
public async acceptInvite(id: GroupInvite) {
|
||||
public async sendInvite(id: GroupSendInvite) {
|
||||
try {
|
||||
return await this.client.groupAcceptInvite(id.inviteCode);
|
||||
const inviteCode = await this.inviteCode({ groupJid: id.groupJid });
|
||||
const inviteUrl = inviteCode.inviteUrl;
|
||||
const numbers = id.numbers.map((number) => this.createJid(number));
|
||||
const description = id.description ?? '';
|
||||
|
||||
const msg = `${description}\n${inviteUrl}`;
|
||||
|
||||
const message = {
|
||||
linkPreview: {
|
||||
text: msg,
|
||||
},
|
||||
};
|
||||
|
||||
for await (const number of numbers) {
|
||||
await this.sendMessageWithTyping(number, message);
|
||||
}
|
||||
|
||||
return { send: true, inviteUrl };
|
||||
} catch (error) {
|
||||
throw new NotFoundException('No invite info', id.inviteCode);
|
||||
throw new NotFoundException('No send invite');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1831,7 +1848,7 @@ export class WAStartupService {
|
||||
update.groupJid,
|
||||
update.expiration,
|
||||
);
|
||||
return { toggleEphemeral: toggleEphemeral };
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
throw new BadRequestException('Error updating setting', error.toString());
|
||||
}
|
||||
|
||||
@@ -34,7 +34,12 @@ export declare namespace wa {
|
||||
profilePictureUrl?: string;
|
||||
};
|
||||
|
||||
export type LocalWebHook = { enabled?: boolean; url?: string; events?: string[] };
|
||||
export type LocalWebHook = {
|
||||
enabled?: boolean;
|
||||
url?: string;
|
||||
events?: string[];
|
||||
webhook_by_events?: boolean;
|
||||
};
|
||||
|
||||
export type StateConnection = {
|
||||
instance?: string;
|
||||
|
||||
Reference in New Issue
Block a user