From efa18bdbb9961ed00b52716ce8d2143428cc72a7 Mon Sep 17 00:00:00 2001 From: Rafael Marinho Date: Wed, 22 Oct 2025 12:29:03 +0200 Subject: [PATCH 1/8] feat(CHA-1391): support addMembers Batch --- src/channel.ts | 9 +++++++++ src/types.ts | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/channel.ts b/src/channel.ts index 47be99ab1..670883325 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -71,6 +71,8 @@ import type { TruncateOptions, UpdateChannelAPIResponse, UpdateChannelOptions, + UpdateChannelsBatchRequest, + UpdateChannelsBatchResponse, UpdateLocationPayload, UserResponse, } from './types'; @@ -614,6 +616,13 @@ export class Channel { }); } + async batch_update(data: UpdateChannelsBatchRequest) { + return await this.getClient().put( + this.getClient().baseURL + '/channels/batch', + data, + ); + } + /** * updatePartial - partial update channel properties * diff --git a/src/types.ts b/src/types.ts index 2057543f5..6dcc56ea2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4446,3 +4446,20 @@ export type EventHook = { created_at?: string; updated_at?: string; }; + +export type UpdateChannelsBatchRequest = { + operation: string; + filter: UpdateChannelsBatchFilters; + members?: string[] | Array; + data?: Partial; +}; + +export type UpdateChannelsBatchFilters = { + cids?: string[]; + channel_types?: string[]; + tags?: Record; +}; + +export type UpdateChannelsBatchResponse = { + result: Record; +} & Partial; From 72008f826c9651df6dd20a0c7698151cc655852e Mon Sep 17 00:00:00 2001 From: Rafael Marinho Date: Wed, 22 Oct 2025 16:42:50 +0200 Subject: [PATCH 2/8] fix(cha-1391): fix to call from server --- src/channel.ts | 9 --------- src/client.ts | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/channel.ts b/src/channel.ts index 670883325..47be99ab1 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -71,8 +71,6 @@ import type { TruncateOptions, UpdateChannelAPIResponse, UpdateChannelOptions, - UpdateChannelsBatchRequest, - UpdateChannelsBatchResponse, UpdateLocationPayload, UserResponse, } from './types'; @@ -616,13 +614,6 @@ export class Channel { }); } - async batch_update(data: UpdateChannelsBatchRequest) { - return await this.getClient().put( - this.getClient().baseURL + '/channels/batch', - data, - ); - } - /** * updatePartial - partial update channel properties * diff --git a/src/client.ts b/src/client.ts index c2cbe8703..bc9c08d34 100644 --- a/src/client.ts +++ b/src/client.ts @@ -209,6 +209,8 @@ import type { TokenOrProvider, TranslateResponse, UnBanUserOptions, + UpdateChannelsBatchRequest, + UpdateChannelsBatchResponse, UpdateChannelTypeRequest, UpdateChannelTypeResponse, UpdateCommandOptions, @@ -4762,4 +4764,17 @@ export class StreamChat { syncDeliveredCandidates(collections: Channel[]) { this.messageDeliveryReporter.syncDeliveredCandidates(collections); } + + /** + * Update Channels Batch + * + * @param {UpdateChannelsBatchRequest} payload for updating channels in batch + * @return {Promise} The server response + */ + async updateChannelsBatch(payload: UpdateChannelsBatchRequest) { + return await this.put( + this.baseURL + `/channels/batch`, + payload, + ); + } } From ba42acce2b478510c68acdb4c7d9ef12cd7f1efb Mon Sep 17 00:00:00 2001 From: javierdfm Date: Fri, 14 Nov 2025 09:25:24 +0100 Subject: [PATCH 3/8] fix: updated channel batch filters --- src/types.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/types.ts b/src/types.ts index 6dcc56ea2..f38101979 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4454,11 +4454,20 @@ export type UpdateChannelsBatchRequest = { data?: Partial; }; -export type UpdateChannelsBatchFilters = { - cids?: string[]; - channel_types?: string[]; - tags?: Record; -}; +export type UpdateChannelsBatchFilters = QueryFilters<{ + cids?: + | RequireOnlyOne, '$in'>> + | RequireOnlyOne, '$eq'>> + | PrimitiveFilter; + types?: + | RequireOnlyOne, '$in'>> + | RequireOnlyOne, '$eq'>> + | PrimitiveFilter; + filter_tags?: + | RequireOnlyOne, '$in'>> + | RequireOnlyOne>, '$eq'>> + | PrimitiveFilter>; +}>; export type UpdateChannelsBatchResponse = { result: Record; From 6d97dee22544d7542cf2b4dc6b60d59fdededecc Mon Sep 17 00:00:00 2001 From: javierdfm Date: Tue, 18 Nov 2025 14:03:58 +0100 Subject: [PATCH 4/8] fix: updated --- src/client.ts | 10 +++++----- src/types.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client.ts b/src/client.ts index bc9c08d34..3f12c5926 100644 --- a/src/client.ts +++ b/src/client.ts @@ -209,7 +209,7 @@ import type { TokenOrProvider, TranslateResponse, UnBanUserOptions, - UpdateChannelsBatchRequest, + UpdateChannelsBatchOptions, UpdateChannelsBatchResponse, UpdateChannelTypeRequest, UpdateChannelTypeResponse, @@ -4768,11 +4768,11 @@ export class StreamChat { /** * Update Channels Batch * - * @param {UpdateChannelsBatchRequest} payload for updating channels in batch - * @return {Promise} The server response + * @param {UpdateChannelsBatchOptions} payload for updating channels in batch + * @return {Promise} The server response */ - async updateChannelsBatch(payload: UpdateChannelsBatchRequest) { - return await this.put( + async updateChannelsBatch(payload: UpdateChannelsBatchOptions) { + return await this.put( this.baseURL + `/channels/batch`, payload, ); diff --git a/src/types.ts b/src/types.ts index 2d08fbf78..d225fe94c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4460,7 +4460,7 @@ export type EventHook = { updated_at?: string; }; -export type UpdateChannelsBatchRequest = { +export type UpdateChannelsBatchOptions = { operation: string; filter: UpdateChannelsBatchFilters; members?: string[] | Array; From 5d76dfb0c14da83628044f54ba439735970366bb Mon Sep 17 00:00:00 2001 From: javierdfm Date: Thu, 11 Dec 2025 11:04:41 +0100 Subject: [PATCH 5/8] fix: updated endpoint params --- src/types.ts | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/types.ts b/src/types.ts index d225fe94c..796f033a4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4460,25 +4460,47 @@ export type EventHook = { updated_at?: string; }; +export type BatchUpdateOperation = + | 'addMembers' + | 'removeMembers' + | 'invites' + | 'assignRoles' + | 'addModerators' + | 'demoteModerators' + | 'hide' + | 'show' + | 'archive' + | 'unarchive' + | 'updateData' + | 'addFilterTags' + | 'removeFilterTags'; + +export type BatchChannelDataUpdate = { + frozen?: boolean; + disabled?: boolean; + custom?: Record; + team?: string; + config_overrides?: Record; + auto_translation_enabled?: boolean; + auto_translation_language?: string; +}; + export type UpdateChannelsBatchOptions = { - operation: string; + operation: BatchUpdateOperation; filter: UpdateChannelsBatchFilters; members?: string[] | Array; - data?: Partial; + data?: BatchChannelDataUpdate; }; export type UpdateChannelsBatchFilters = QueryFilters<{ cids?: - | RequireOnlyOne, '$in'>> - | RequireOnlyOne, '$eq'>> + | RequireOnlyOne, '$in' | '$eq'>> | PrimitiveFilter; types?: - | RequireOnlyOne, '$in'>> - | RequireOnlyOne, '$eq'>> + | RequireOnlyOne, '$in' | '$eq'>> | PrimitiveFilter; filter_tags?: - | RequireOnlyOne, '$in'>> - | RequireOnlyOne>, '$eq'>> + | RequireOnlyOne, '$in' | '$eq'>> | PrimitiveFilter>; }>; From 25fb44fa540f12b4dbeb14ec2801706c60fc2455 Mon Sep 17 00:00:00 2001 From: javierdfm Date: Tue, 16 Dec 2025 15:05:36 +0100 Subject: [PATCH 6/8] feat: added channel batch updater --- src/channel_batch_updater.ts | 218 +++++++++++++++++++++++++++++++++++ src/client.ts | 10 ++ src/index.ts | 1 + src/types.ts | 1 + 4 files changed, 230 insertions(+) create mode 100644 src/channel_batch_updater.ts diff --git a/src/channel_batch_updater.ts b/src/channel_batch_updater.ts new file mode 100644 index 000000000..f648c53eb --- /dev/null +++ b/src/channel_batch_updater.ts @@ -0,0 +1,218 @@ +import type { StreamChat } from './client'; +import type { + APIResponse, + BatchChannelDataUpdate, + NewMemberPayload, + UpdateChannelsBatchFilters, + UpdateChannelsBatchResponse, +} from './types'; + +/** + * ChannelBatchUpdater - A class that provides convenience methods for batch channel operations + */ +export class ChannelBatchUpdater { + client: StreamChat; + + constructor(client: StreamChat) { + this.client = client; + } + + // Member operations + + /** + * addMembers - Add members to channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @param {string[] | NewMemberPayload[]} members Members to add + * @return {Promise} The server response + */ + async addMembers( + filter: UpdateChannelsBatchFilters, + members: string[] | NewMemberPayload[], + ) { + return await this.client.updateChannelsBatch({ + operation: 'addMembers', + filter, + members, + }); + } + + /** + * removeMembers - Remove members from channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @param {string[]} members Member IDs to remove + * @return {Promise} The server response + */ + async removeMembers(filter: UpdateChannelsBatchFilters, members: string[]) { + return await this.client.updateChannelsBatch({ + operation: 'removeMembers', + filter, + members, + }); + } + + /** + * inviteMembers - Invite members to channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @param {string[] | NewMemberPayload[]} members Members to invite + * @return {Promise} The server response + */ + async inviteMembers( + filter: UpdateChannelsBatchFilters, + members: string[] | NewMemberPayload[], + ) { + return await this.client.updateChannelsBatch({ + operation: 'invites', + filter, + members, + }); + } + + /** + * addModerators - Add moderators to channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @param {string[]} members Member IDs to promote to moderator + * @return {Promise} The server response + */ + async addModerators(filter: UpdateChannelsBatchFilters, members: string[]) { + return await this.client.updateChannelsBatch({ + operation: 'addModerators', + filter, + members, + }); + } + + /** + * demoteModerators - Remove moderator role from members in channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @param {string[]} members Member IDs to demote + * @return {Promise} The server response + */ + async demoteModerators(filter: UpdateChannelsBatchFilters, members: string[]) { + return await this.client.updateChannelsBatch({ + operation: 'demoteModerators', + filter, + members, + }); + } + + /** + * assignRoles - Assign roles to members in channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @param {NewMemberPayload[]} members Members with role assignments + * @return {Promise} The server response + */ + async assignRoles(filter: UpdateChannelsBatchFilters, members: NewMemberPayload[]) { + return await this.client.updateChannelsBatch({ + operation: 'assignRoles', + filter, + members, + }); + } + + // Visibility operations + + /** + * hide - Hide channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @return {Promise} The server response + */ + async hide(filter: UpdateChannelsBatchFilters) { + return await this.client.updateChannelsBatch({ + operation: 'hide', + filter, + }); + } + + /** + * show - Show channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @return {Promise} The server response + */ + async show(filter: UpdateChannelsBatchFilters) { + return await this.client.updateChannelsBatch({ + operation: 'show', + filter, + }); + } + + /** + * archive - Archive channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @return {Promise} The server response + */ + async archive(filter: UpdateChannelsBatchFilters) { + return await this.client.updateChannelsBatch({ + operation: 'archive', + filter, + }); + } + + /** + * unarchive - Unarchive channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @return {Promise} The server response + */ + async unarchive(filter: UpdateChannelsBatchFilters) { + return await this.client.updateChannelsBatch({ + operation: 'unarchive', + filter, + }); + } + + // Data operations + + /** + * updateData - Update data on channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @param {BatchChannelDataUpdate} data Data to update + * @return {Promise} The server response + */ + async updateData(filter: UpdateChannelsBatchFilters, data: BatchChannelDataUpdate) { + return await this.client.updateChannelsBatch({ + operation: 'updateData', + filter, + data, + }); + } + + /** + * addFilterTags - Add filter tags to channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @param {string[]} tags Tags to add + * @return {Promise} The server response + */ + async addFilterTags(filter: UpdateChannelsBatchFilters, tags: string[]) { + return await this.client.updateChannelsBatch({ + operation: 'addFilterTags', + filter, + filter_tags_update: tags, + }); + } + + /** + * removeFilterTags - Remove filter tags from channels matching the filter + * + * @param {UpdateChannelsBatchFilters} filter Filter to select channels + * @param {string[]} tags Tags to remove + * @return {Promise} The server response + */ + async removeFilterTags(filter: UpdateChannelsBatchFilters, tags: string[]) { + return await this.client.updateChannelsBatch({ + operation: 'removeFilterTags', + filter, + filter_tags_update: tags, + }); + } +} diff --git a/src/client.ts b/src/client.ts index 3f12c5926..cf0f365d7 100644 --- a/src/client.ts +++ b/src/client.ts @@ -13,6 +13,7 @@ import { CheckSignature, DevToken, JWTUserToken } from './signing'; import { TokenManager } from './token_manager'; import { WSConnectionFallback } from './connection_fallback'; import { Campaign } from './campaign'; +import { ChannelBatchUpdater } from './channel_batch_updater'; import { Segment } from './segment'; import { isErrorResponse, isWSFailure } from './errors'; import { @@ -3718,6 +3719,15 @@ export class StreamChat { return new Campaign(this, idOrData, data); } + /** + * channelBatchUpdater - Returns a ChannelBatchUpdater instance for batch channel operations + * + * @return {ChannelBatchUpdater} A ChannelBatchUpdater instance + */ + channelBatchUpdater() { + return new ChannelBatchUpdater(this); + } + segment(type: SegmentType, idOrData: string | SegmentData, data?: SegmentData) { if (typeof idOrData === 'string') { return new Segment(this, type, idOrData, data); diff --git a/src/index.ts b/src/index.ts index 5f5daf375..805a12979 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ export * from './base64'; export * from './campaign'; +export * from './channel_batch_updater'; export * from './client'; export * from './client_state'; export * from './channel'; diff --git a/src/types.ts b/src/types.ts index 796f033a4..7311f9018 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4490,6 +4490,7 @@ export type UpdateChannelsBatchOptions = { filter: UpdateChannelsBatchFilters; members?: string[] | Array; data?: BatchChannelDataUpdate; + filter_tags_update?: string[]; }; export type UpdateChannelsBatchFilters = QueryFilters<{ From abab045d6a59075ac4dc5e54ce3a738b385ff861 Mon Sep 17 00:00:00 2001 From: Rafael Marinho Date: Thu, 18 Dec 2025 08:11:03 +0100 Subject: [PATCH 7/8] fix(CHA-1391): add skip for no-unused-vars --- src/channel_batch_updater.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/channel_batch_updater.ts b/src/channel_batch_updater.ts index f648c53eb..86dc15d4d 100644 --- a/src/channel_batch_updater.ts +++ b/src/channel_batch_updater.ts @@ -1,3 +1,5 @@ +/* eslint no-unused-vars: "off" */ + import type { StreamChat } from './client'; import type { APIResponse, From bd11ab9dce79e8c4db88f175a1bd94a1a0e98443 Mon Sep 17 00:00:00 2001 From: Rafael Marinho Date: Thu, 18 Dec 2025 08:19:51 +0100 Subject: [PATCH 8/8] fix(cha-1494): fix lint --- src/channel_batch_updater.ts | 57 ++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/src/channel_batch_updater.ts b/src/channel_batch_updater.ts index 86dc15d4d..3d37c9a6f 100644 --- a/src/channel_batch_updater.ts +++ b/src/channel_batch_updater.ts @@ -1,5 +1,3 @@ -/* eslint no-unused-vars: "off" */ - import type { StreamChat } from './client'; import type { APIResponse, @@ -31,7 +29,7 @@ export class ChannelBatchUpdater { async addMembers( filter: UpdateChannelsBatchFilters, members: string[] | NewMemberPayload[], - ) { + ): Promise { return await this.client.updateChannelsBatch({ operation: 'addMembers', filter, @@ -46,7 +44,10 @@ export class ChannelBatchUpdater { * @param {string[]} members Member IDs to remove * @return {Promise} The server response */ - async removeMembers(filter: UpdateChannelsBatchFilters, members: string[]) { + async removeMembers( + filter: UpdateChannelsBatchFilters, + members: string[], + ): Promise { return await this.client.updateChannelsBatch({ operation: 'removeMembers', filter, @@ -64,7 +65,7 @@ export class ChannelBatchUpdater { async inviteMembers( filter: UpdateChannelsBatchFilters, members: string[] | NewMemberPayload[], - ) { + ): Promise { return await this.client.updateChannelsBatch({ operation: 'invites', filter, @@ -79,7 +80,10 @@ export class ChannelBatchUpdater { * @param {string[]} members Member IDs to promote to moderator * @return {Promise} The server response */ - async addModerators(filter: UpdateChannelsBatchFilters, members: string[]) { + async addModerators( + filter: UpdateChannelsBatchFilters, + members: string[], + ): Promise { return await this.client.updateChannelsBatch({ operation: 'addModerators', filter, @@ -94,7 +98,10 @@ export class ChannelBatchUpdater { * @param {string[]} members Member IDs to demote * @return {Promise} The server response */ - async demoteModerators(filter: UpdateChannelsBatchFilters, members: string[]) { + async demoteModerators( + filter: UpdateChannelsBatchFilters, + members: string[], + ): Promise { return await this.client.updateChannelsBatch({ operation: 'demoteModerators', filter, @@ -109,7 +116,10 @@ export class ChannelBatchUpdater { * @param {NewMemberPayload[]} members Members with role assignments * @return {Promise} The server response */ - async assignRoles(filter: UpdateChannelsBatchFilters, members: NewMemberPayload[]) { + async assignRoles( + filter: UpdateChannelsBatchFilters, + members: NewMemberPayload[], + ): Promise { return await this.client.updateChannelsBatch({ operation: 'assignRoles', filter, @@ -125,7 +135,9 @@ export class ChannelBatchUpdater { * @param {UpdateChannelsBatchFilters} filter Filter to select channels * @return {Promise} The server response */ - async hide(filter: UpdateChannelsBatchFilters) { + async hide( + filter: UpdateChannelsBatchFilters, + ): Promise { return await this.client.updateChannelsBatch({ operation: 'hide', filter, @@ -138,7 +150,9 @@ export class ChannelBatchUpdater { * @param {UpdateChannelsBatchFilters} filter Filter to select channels * @return {Promise} The server response */ - async show(filter: UpdateChannelsBatchFilters) { + async show( + filter: UpdateChannelsBatchFilters, + ): Promise { return await this.client.updateChannelsBatch({ operation: 'show', filter, @@ -151,7 +165,9 @@ export class ChannelBatchUpdater { * @param {UpdateChannelsBatchFilters} filter Filter to select channels * @return {Promise} The server response */ - async archive(filter: UpdateChannelsBatchFilters) { + async archive( + filter: UpdateChannelsBatchFilters, + ): Promise { return await this.client.updateChannelsBatch({ operation: 'archive', filter, @@ -164,7 +180,9 @@ export class ChannelBatchUpdater { * @param {UpdateChannelsBatchFilters} filter Filter to select channels * @return {Promise} The server response */ - async unarchive(filter: UpdateChannelsBatchFilters) { + async unarchive( + filter: UpdateChannelsBatchFilters, + ): Promise { return await this.client.updateChannelsBatch({ operation: 'unarchive', filter, @@ -180,7 +198,10 @@ export class ChannelBatchUpdater { * @param {BatchChannelDataUpdate} data Data to update * @return {Promise} The server response */ - async updateData(filter: UpdateChannelsBatchFilters, data: BatchChannelDataUpdate) { + async updateData( + filter: UpdateChannelsBatchFilters, + data: BatchChannelDataUpdate, + ): Promise { return await this.client.updateChannelsBatch({ operation: 'updateData', filter, @@ -195,7 +216,10 @@ export class ChannelBatchUpdater { * @param {string[]} tags Tags to add * @return {Promise} The server response */ - async addFilterTags(filter: UpdateChannelsBatchFilters, tags: string[]) { + async addFilterTags( + filter: UpdateChannelsBatchFilters, + tags: string[], + ): Promise { return await this.client.updateChannelsBatch({ operation: 'addFilterTags', filter, @@ -210,7 +234,10 @@ export class ChannelBatchUpdater { * @param {string[]} tags Tags to remove * @return {Promise} The server response */ - async removeFilterTags(filter: UpdateChannelsBatchFilters, tags: string[]) { + async removeFilterTags( + filter: UpdateChannelsBatchFilters, + tags: string[], + ): Promise { return await this.client.updateChannelsBatch({ operation: 'removeFilterTags', filter,