Support
Quality
Security
License
Reuse
kandi has reviewed ChatKit and discovered the below as its top functions. This is intended to give you an instant insight into ChatKit implemented functionality, and help decide if they suit your requirements.
Ready-to-use already styled solution for quick implementation;
Default and custom media messages;
Fully customizable layouts - setting styles out of the box (use your own colors, text appearances, drawables, selectors and sizes) or even create your own custom markup or/and holders for unique behaviour;
List of dialogs, including tete-a-tete and group chats, markers for unread messages and last user message view;
List of messages (incoming and outcoming) with history pagination and already calculated dates headers;
Different avatars with no specific realization of image loading - you can use any library you want;
Selection mode for interacting with messages;
Links highlighting
Easy dates formatting;
Your own models for dialogs and messages - there is no converting needed;
Ready to use message input view;
Custom animations (according to RecyclerView usage).
Download
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
AndroidX
android.useAndroidX=true
android.enableJetifier=true
Proguard
-keep class * extends com.stfalcon.chatkit.messages.MessageHolders$OutcomingTextMessageViewHolder {
public <init>(android.view.View, java.lang.Object);
public <init>(android.view.View);
}
-keep class * extends com.stfalcon.chatkit.messages.MessageHolders$IncomingTextMessageViewHolder {
public <init>(android.view.View, java.lang.Object);
public <init>(android.view.View);
}
-keep class * extends com.stfalcon.chatkit.messages.MessageHolders$IncomingImageMessageViewHolder {
public <init>(android.view.View, java.lang.Object);
public <init>(android.view.View);
}
-keep class * extends com.stfalcon.chatkit.messages.MessageHolders$OutcomingImageMessageViewHolder {
public <init>(android.view.View, java.lang.Object);
public <init>(android.view.View);
}
License
Copyright (C) 2017 stfalcon.com
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
https://github.com/stfalcon-studio/ChatKit/blob/master/LICENSE
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.
Stream.io Chat API - Member state omitted from queryChannels response
public async getChannelUserIds(channelId: string): Promise<string[]> {
try {
const queryChannelsResponse = await this.client.queryChannels(
{ id: channelId },
{ last_updated_at: -1 },
{ message_limit: 0, limit: 1, state: true },
)
const channel = queryChannelsResponse[0]
const userIds = Object.keys(channel.state.members)
console.log('userIds:', userIds)
return userIds
} catch (err) {
throw new InternalServerErrorException(
`Error getting user ids for channel ('${channelId}').`,
err,
)
}
}
public async getOfflineUserIds(userIds: string[]): Promise<string[]> {
try {
const queryUsersResponse = await this.client.queryUsers(
{ id: { $in: userIds } },
{ last_active: -1 },
{},
)
console.log('queryUsersResponse:', queryUsersResponse)
const offlineUserIds = queryUsersResponse.users
.filter(u => !u.online)
.map(u => u.user_id)
return offlineUserIds
} catch (err) {
throw new InternalServerErrorException(
`Error getting offline user ids from ('${JSON.stringify(
userIds,
null,
2,
)}').`,
err,
)
}
}
@Post('stream/messages')
public async onReceive(
@Req() req: Request,
@Headers('x-signature') signature: string,
@Body() body: any,
) {
try {
console.debug('webhooks-stream.messages.onReceive')
this.chatService.verifyWebhook((req as any).rawBody, signature)
console.log('DEBUG WEBHOOK BODY', JSON.stringify(body, null, 2))
switch (body.type) {
case 'message.new': {
const offlineMemberIds = await this.chatService.getOfflineUserIds(
body.members.map(member => member.user_id),
)
...
-----------------------
public async getChannelUserIds(channelId: string): Promise<string[]> {
try {
const queryChannelsResponse = await this.client.queryChannels(
{ id: channelId },
{ last_updated_at: -1 },
{ message_limit: 0, limit: 1, state: true },
)
const channel = queryChannelsResponse[0]
const userIds = Object.keys(channel.state.members)
console.log('userIds:', userIds)
return userIds
} catch (err) {
throw new InternalServerErrorException(
`Error getting user ids for channel ('${channelId}').`,
err,
)
}
}
public async getOfflineUserIds(userIds: string[]): Promise<string[]> {
try {
const queryUsersResponse = await this.client.queryUsers(
{ id: { $in: userIds } },
{ last_active: -1 },
{},
)
console.log('queryUsersResponse:', queryUsersResponse)
const offlineUserIds = queryUsersResponse.users
.filter(u => !u.online)
.map(u => u.user_id)
return offlineUserIds
} catch (err) {
throw new InternalServerErrorException(
`Error getting offline user ids from ('${JSON.stringify(
userIds,
null,
2,
)}').`,
err,
)
}
}
@Post('stream/messages')
public async onReceive(
@Req() req: Request,
@Headers('x-signature') signature: string,
@Body() body: any,
) {
try {
console.debug('webhooks-stream.messages.onReceive')
this.chatService.verifyWebhook((req as any).rawBody, signature)
console.log('DEBUG WEBHOOK BODY', JSON.stringify(body, null, 2))
switch (body.type) {
case 'message.new': {
const offlineMemberIds = await this.chatService.getOfflineUserIds(
body.members.map(member => member.user_id),
)
...
-----------------------
public async getChannelUserIds(channelId: string): Promise<string[]> {
try {
const queryChannelsResponse = await this.client.queryChannels(
{ id: channelId },
{ last_updated_at: -1 },
{ message_limit: 0, limit: 1, state: true },
)
const channel = queryChannelsResponse[0]
const userIds = Object.keys(channel.state.members)
console.log('userIds:', userIds)
return userIds
} catch (err) {
throw new InternalServerErrorException(
`Error getting user ids for channel ('${channelId}').`,
err,
)
}
}
public async getOfflineUserIds(userIds: string[]): Promise<string[]> {
try {
const queryUsersResponse = await this.client.queryUsers(
{ id: { $in: userIds } },
{ last_active: -1 },
{},
)
console.log('queryUsersResponse:', queryUsersResponse)
const offlineUserIds = queryUsersResponse.users
.filter(u => !u.online)
.map(u => u.user_id)
return offlineUserIds
} catch (err) {
throw new InternalServerErrorException(
`Error getting offline user ids from ('${JSON.stringify(
userIds,
null,
2,
)}').`,
err,
)
}
}
@Post('stream/messages')
public async onReceive(
@Req() req: Request,
@Headers('x-signature') signature: string,
@Body() body: any,
) {
try {
console.debug('webhooks-stream.messages.onReceive')
this.chatService.verifyWebhook((req as any).rawBody, signature)
console.log('DEBUG WEBHOOK BODY', JSON.stringify(body, null, 2))
switch (body.type) {
case 'message.new': {
const offlineMemberIds = await this.chatService.getOfflineUserIds(
body.members.map(member => member.user_id),
)
...
Debugging deploying full-stack app to Heroku
app.post('/authenticate', (req, res) => {
const authData = chatkit.authenticate({ userId: req.query.user_id })
res.status(authData.status).send(authData.body)
})
// if (process.env.NODE_ENV === 'production') { // This is your thang, I commented it out to reach a solution, but probably not necessary for you
app.use(express.static('build')); // 'react-scripts build' places a /build directory at root, not /client/build
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'build', 'index.html')); // Removed client here also
});
// }
"scripts": {
"start": "node server.js",
"build": "react-scripts build",
"postinstall": "npm run build"
},
-----------------------
app.post('/authenticate', (req, res) => {
const authData = chatkit.authenticate({ userId: req.query.user_id })
res.status(authData.status).send(authData.body)
})
// if (process.env.NODE_ENV === 'production') { // This is your thang, I commented it out to reach a solution, but probably not necessary for you
app.use(express.static('build')); // 'react-scripts build' places a /build directory at root, not /client/build
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'build', 'index.html')); // Removed client here also
});
// }
"scripts": {
"start": "node server.js",
"build": "react-scripts build",
"postinstall": "npm run build"
},
Stripe Firebase Cloud Functions - res.send() is not a function
exports.stripeCreateOathResponseToken =
functions.https.onRequest((req, res) => {
return cors(req, res, () => {
// put your function code here
});
});
How to send document or images in group using plain javascript pusher chatkit
currentUser.sendMultipartMessage({
roomId: myRoom.id,
parts: [
{ type: "text/plain", content: "" },
{
type: "image/gif",
url: "https://gfycat.com/failingforkedheterodontosaurus",
},
{
file: document.querySelector("#attach").files[0],
customData: { metadata: 42 },
}
],
});
How to show badges using pusher data
currentUser.subscribeToRoomMultipart({
roomId: this.currentRoom,
messageLimit: 100,
hooks: {
//Message is received
onMessage: message => {
this.lastMessage = message.id;
//set cursor
currentUser.setReadCursor({
roomId: this.currentRoom,
position: this.lastMessage
})
.then(() => {
console.log(`Set cursor to message ${this.lastMessage}`)
})
.catch(err => {
console.log(`Error setting cursor: ${err}`)
})
}
},
}).then(room => {
//log unread count
console.log(`Unread count: ${room.unreadCount}`)
});
kotlin.UninitializedPropertyAccessException: lateinit property roomClickedInterface has not been initialized
override fun onClick(p0: View?) {
if(this::roomClickedInterface.isInitialized) {
roomClickedInterface.roomSelected(list[adapterPosition])
}
}
//lateinit var roomClickedInterface:RoomClickedInterface
class ChatRoomsListAdapter(val roomClickedInterface:RoomClickedInterface):
RecyclerView.Adapter<ChatRoomsListAdapter.ViewHolder>() {
//Implementation
}
private var roomClickedInterface: RoomClickedInterface? = null
override fun onClick(p0: View?) {
roomClickedInterface?.roomSelected(list[adapterPosition])
}
-----------------------
override fun onClick(p0: View?) {
if(this::roomClickedInterface.isInitialized) {
roomClickedInterface.roomSelected(list[adapterPosition])
}
}
//lateinit var roomClickedInterface:RoomClickedInterface
class ChatRoomsListAdapter(val roomClickedInterface:RoomClickedInterface):
RecyclerView.Adapter<ChatRoomsListAdapter.ViewHolder>() {
//Implementation
}
private var roomClickedInterface: RoomClickedInterface? = null
override fun onClick(p0: View?) {
roomClickedInterface?.roomSelected(list[adapterPosition])
}
-----------------------
override fun onClick(p0: View?) {
if(this::roomClickedInterface.isInitialized) {
roomClickedInterface.roomSelected(list[adapterPosition])
}
}
//lateinit var roomClickedInterface:RoomClickedInterface
class ChatRoomsListAdapter(val roomClickedInterface:RoomClickedInterface):
RecyclerView.Adapter<ChatRoomsListAdapter.ViewHolder>() {
//Implementation
}
private var roomClickedInterface: RoomClickedInterface? = null
override fun onClick(p0: View?) {
roomClickedInterface?.roomSelected(list[adapterPosition])
}
-----------------------
override fun onClick(p0: View?) {
if(this::roomClickedInterface.isInitialized) {
roomClickedInterface.roomSelected(list[adapterPosition])
}
}
//lateinit var roomClickedInterface:RoomClickedInterface
class ChatRoomsListAdapter(val roomClickedInterface:RoomClickedInterface):
RecyclerView.Adapter<ChatRoomsListAdapter.ViewHolder>() {
//Implementation
}
private var roomClickedInterface: RoomClickedInterface? = null
override fun onClick(p0: View?) {
roomClickedInterface?.roomSelected(list[adapterPosition])
}
-----------------------
override fun onClick(p0: View?) {
if(this::roomClickedInterface.isInitialized) {
roomClickedInterface.roomSelected(list[adapterPosition])
}
}
//lateinit var roomClickedInterface:RoomClickedInterface
class ChatRoomsListAdapter(val roomClickedInterface:RoomClickedInterface):
RecyclerView.Adapter<ChatRoomsListAdapter.ViewHolder>() {
//Implementation
}
private var roomClickedInterface: RoomClickedInterface? = null
override fun onClick(p0: View?) {
roomClickedInterface?.roomSelected(list[adapterPosition])
}
React.js Duplicate key and Unreachable code BUGS
return (
<div>
</div>
)
const myObject = {
currentUsername: 'bob',
currentUsername: 'alice',
}
<ChatScreen
currentUsername="bob"
currentUsername="alice"
/>
-----------------------
return (
<div>
</div>
)
const myObject = {
currentUsername: 'bob',
currentUsername: 'alice',
}
<ChatScreen
currentUsername="bob"
currentUsername="alice"
/>
-----------------------
return (
<div>
</div>
)
const myObject = {
currentUsername: 'bob',
currentUsername: 'alice',
}
<ChatScreen
currentUsername="bob"
currentUsername="alice"
/>
-----------------------
constructor() {
super();
this.state = {
currentUsername: '',
currentUsername: 'WhatIsYournameScreen',
}
this.onUsernameSubmitted = this.onUsernameSubmitted.bind(this)
}
import React, { Component } from 'react'
import Chatkit from '@pusher/chatkit-client'
class ChatScreen extends Component{
constructor(props) {
super(props)
this.state = {
currentUser: {}
}
}
componentDidMount() {
const chatManager = new Chatkit.ChatManager({
instanceLocator: 'v1:us1:1308c34d-00c3-4257-b1c3-647cf56fd5fa',
userId: this.props.currentUsername,
tokenProvider: new Chatkit.TokenProvider({
url: 'http://localhost:3000/authenticate',
}),
})
chatManager
.connect()
.then(currentUser => {
this.setState({ currentUser })
})
.catch(error => console.error('error', error))
}
render() {
return (
<div style={styles.container}>
<div style={styles.chatContainer}>
<aside style={styles.whosOnlineListContainer}>
<h2>Who's online PLACEHOLDER</h2>
</aside>
<section style={styles.chatListContainer}>
<h2>Chat PLACEHOLDER</h2>
</section>
</div>
</div>
);
}
}
const styles = {
container: {
height: '100vh',
display: 'flex',
flexDirection: 'column',
},
chatContainer: {
display: 'flex',
flex: 1,
},
whosOnlineListContainer: {
width: '300px',
flex: 'none',
padding: 20,
backgroundColor: '#2c303b',
color: 'white',
},
chatListContainer: {
padding: 20,
width: '85%',
display: 'flex',
flexDirection: 'column',
},
}
export default ChatScreen;
-----------------------
constructor() {
super();
this.state = {
currentUsername: '',
currentUsername: 'WhatIsYournameScreen',
}
this.onUsernameSubmitted = this.onUsernameSubmitted.bind(this)
}
import React, { Component } from 'react'
import Chatkit from '@pusher/chatkit-client'
class ChatScreen extends Component{
constructor(props) {
super(props)
this.state = {
currentUser: {}
}
}
componentDidMount() {
const chatManager = new Chatkit.ChatManager({
instanceLocator: 'v1:us1:1308c34d-00c3-4257-b1c3-647cf56fd5fa',
userId: this.props.currentUsername,
tokenProvider: new Chatkit.TokenProvider({
url: 'http://localhost:3000/authenticate',
}),
})
chatManager
.connect()
.then(currentUser => {
this.setState({ currentUser })
})
.catch(error => console.error('error', error))
}
render() {
return (
<div style={styles.container}>
<div style={styles.chatContainer}>
<aside style={styles.whosOnlineListContainer}>
<h2>Who's online PLACEHOLDER</h2>
</aside>
<section style={styles.chatListContainer}>
<h2>Chat PLACEHOLDER</h2>
</section>
</div>
</div>
);
}
}
const styles = {
container: {
height: '100vh',
display: 'flex',
flexDirection: 'column',
},
chatContainer: {
display: 'flex',
flex: 1,
},
whosOnlineListContainer: {
width: '300px',
flex: 'none',
padding: 20,
backgroundColor: '#2c303b',
color: 'white',
},
chatListContainer: {
padding: 20,
width: '85%',
display: 'flex',
flexDirection: 'column',
},
}
export default ChatScreen;
-----------------------
import React, { Component } from 'react'
import Chatkit from '@pusher/chatkit-client'
class ChatScreen extends Component{
constructor(props) {
super(props)
this.state = {
currentUser: {}
}
}
componentDidMount() {
const chatManager = new Chatkit.ChatManager({
instanceLocator: 'v1:us1:1308c34d-00c3-4257-b1c3-647cf56fd5fa',
userId: this.props.currentUsername,
tokenProvider: new Chatkit.TokenProvider({
url: 'http://localhost:3000/authenticate',
}),
})
chatManager
.connect()
.then(currentUser => {
this.setState({ currentUser })
})
.catch(error => console.error('error', error))
}
render() {
const styles = {
container: {
height: '100vh',
display: 'flex',
flexDirection: 'column',
},
chatContainer: {
display: 'flex',
flex: 1,
},
whosOnlineListContainer: {
width: '300px',
flex: 'none',
padding: 20,
backgroundColor: '#2c303b',
color: 'white',
},
chatListContainer: {
padding: 20,
width: '85%',
display: 'flex',
flexDirection: 'column',
},
}
return (
<div style={styles.container}>
<div style={styles.chatContainer}>
<aside style={styles.whosOnlineListContainer}>
<h2>Who's online PLACEHOLDER</h2>
</aside>
<section style={styles.chatListContainer}>
<h2>Chat PLACEHOLDER</h2>
</section>
</div>
</div>
);
}
}
export default ChatScreen;
import React, { Component } from 'react'
import UsernameForm from './components/UsernameForm'
import ChatScreen from './components/ChatScreen'
class App extends Component {
constructor() {
super();
this.state = {
currentUsername: '',
currentScreen: 'WhatIsYournameScreen',
}
this.onUsernameSubmitted = this.onUsernameSubmitted.bind(this)
}
onUsernameSubmitted(username) {
fetch('http://localhost:3000/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username }),
})
.then(response => {
this.setState({
currentUsername: username,
currentScreen: 'ChatScreen'
})
})
.catch(error => console.error('error', error))
}
render() {
if (this.state.currentScreen === 'WhatIsYourUsernameScreen') {
return <UsernameForm onSubmit={this.onUsernameSubmitted} />
}
if (this.state.currentScreen === 'ChatScreen') {
return <ChatScreen currentUsername={this.state.currentUsername} />
}
return (
<div>
<h1>Hoş geldin</h1>
<UsernameForm onSubmit={this.onUsernameSubmitted} />
</div>
);
}
}
export default App;
-----------------------
import React, { Component } from 'react'
import Chatkit from '@pusher/chatkit-client'
class ChatScreen extends Component{
constructor(props) {
super(props)
this.state = {
currentUser: {}
}
}
componentDidMount() {
const chatManager = new Chatkit.ChatManager({
instanceLocator: 'v1:us1:1308c34d-00c3-4257-b1c3-647cf56fd5fa',
userId: this.props.currentUsername,
tokenProvider: new Chatkit.TokenProvider({
url: 'http://localhost:3000/authenticate',
}),
})
chatManager
.connect()
.then(currentUser => {
this.setState({ currentUser })
})
.catch(error => console.error('error', error))
}
render() {
const styles = {
container: {
height: '100vh',
display: 'flex',
flexDirection: 'column',
},
chatContainer: {
display: 'flex',
flex: 1,
},
whosOnlineListContainer: {
width: '300px',
flex: 'none',
padding: 20,
backgroundColor: '#2c303b',
color: 'white',
},
chatListContainer: {
padding: 20,
width: '85%',
display: 'flex',
flexDirection: 'column',
},
}
return (
<div style={styles.container}>
<div style={styles.chatContainer}>
<aside style={styles.whosOnlineListContainer}>
<h2>Who's online PLACEHOLDER</h2>
</aside>
<section style={styles.chatListContainer}>
<h2>Chat PLACEHOLDER</h2>
</section>
</div>
</div>
);
}
}
export default ChatScreen;
import React, { Component } from 'react'
import UsernameForm from './components/UsernameForm'
import ChatScreen from './components/ChatScreen'
class App extends Component {
constructor() {
super();
this.state = {
currentUsername: '',
currentScreen: 'WhatIsYournameScreen',
}
this.onUsernameSubmitted = this.onUsernameSubmitted.bind(this)
}
onUsernameSubmitted(username) {
fetch('http://localhost:3000/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username }),
})
.then(response => {
this.setState({
currentUsername: username,
currentScreen: 'ChatScreen'
})
})
.catch(error => console.error('error', error))
}
render() {
if (this.state.currentScreen === 'WhatIsYourUsernameScreen') {
return <UsernameForm onSubmit={this.onUsernameSubmitted} />
}
if (this.state.currentScreen === 'ChatScreen') {
return <ChatScreen currentUsername={this.state.currentUsername} />
}
return (
<div>
<h1>Hoş geldin</h1>
<UsernameForm onSubmit={this.onUsernameSubmitted} />
</div>
);
}
}
export default App;
QUESTION
Stream.io Chat API - Member state omitted from queryChannels response
Asked 2020-Apr-07 at 12:44When I do the following:
const queryChannelsResponse = await this.client.queryChannels(
{ id: channelId },
{ last_updated_at: -1 },
{ state: true },
This does not include the members. How am I able to get the member information?
I am writing a webhook and I want it to send a push notification (I am currently sending these myself via expo) to all offline users.
I am migrating from pusher chatkit which is now being discontinued. They had a new_message_users_offline hook for this purpose.
In the message.new webhook payload in the documentation the members are present but they are not present in the request body:
{
"type": "message.new",
"cid": "messaging:394f36fd-d512-4f2b-a785-ab8dfe82af49",
"message": {
"id": "f73ee1a8-f6fd-450b-bc64-0840b4df8fd9-2b4908ad-e267-4c48-8f41-8c26c8f769ce",
"text": "Ffddf",
"html": "<p>Ffddf</p>\n",
"type": "regular",
"user": {
"id": "f73ee1a8-f6fd-450b-bc64-0840b4df8fd9",
"role": "user",
"created_at": "2020-04-06T14:06:37.979584Z",
"updated_at": "2020-04-06T19:45:39.556842Z",
"last_active": "2020-04-06T19:45:39.54939Z",
"banned": false,
"online": true,
"name": "Mark Everett",
"image": "https://8dc-user-files-dev.s3.eu-west-1.amazonaws.com/MEMBER_PROFILE_IMAGE-f73ee1a8-f6fd-450b-bc64-0840b4df8fd9.png?v=6"
},
"attachments": [],
"latest_reactions": [],
"own_reactions": [],
"reaction_counts": null,
"reaction_scores": {},
"reply_count": 0,
"created_at": "2020-04-06T19:51:14.114803Z",
"updated_at": "2020-04-06T19:51:14.114803Z",
"mentioned_users": []
},
"user": {
"id": "f73ee1a8-f6fd-450b-bc64-0840b4df8fd9",
"role": "user",
"created_at": "2020-04-06T14:06:37.979584Z",
"updated_at": "2020-04-06T19:45:39.556842Z",
"last_active": "2020-04-06T19:45:39.54939Z",
"banned": false,
"online": true,
"channel_unread_count": 0,
"channel_last_read_at": "1970-01-01T00:00:00Z",
"total_unread_count": 0,
"unread_channels": 0,
"unread_count": 0,
"image": "https://8dc-user-files-dev.s3.eu-west-1.amazonaws.com/MEMBER_PROFILE_IMAGE-f73ee1a8-f6fd-450b-bc64-0840b4df8fd9.png?v=6",
"name": "Mark Everett"
},
"watcher_count": 1,
"created_at": "2020-04-06T19:51:14.121213459Z",
"channel_type": "messaging",
"channel_id": "394f36fd-d512-4f2b-a785-ab8dfe82af49"
}
My plan is do do something like this:
public async getOfflineUserIds(channelId: string): Promise<string[]> {
try {
// Get the channel
const queryChannelsResponse = await this.client.queryChannels(
{ id: channelId },
{ last_updated_at: -1 },
{ message_limit: 0, limit: 1, state: true},
)
const channel = queryChannelsResponse[0]
console.log('channel: ', channel)
// Get the channels members
const userIds: string[] = []
// tslint:disable-next-line: forin
for (const index in channel.state.members) {
userIds.push(channel.state.members[index].user_id)
}
console.log('userIds:', userIds)
const queryUsersResponse = await this.client.queryUsers(
{ id: { $in: userIds } },
{ last_active: -1 },
{},
)
console.log('queryUsersResponse:', queryUsersResponse)
// Work out who is offline/online
const offlineUserIds = queryUsersResponse.users
.filter(u => !u.online)
.map(u => u.id)
return offlineUserIds
} catch (err) {
throw new InternalServerErrorException(
'Error getting offline users for channel.',
err,
)
}
}
ANSWER
Answered 2020-Apr-07 at 12:44This is now resolved.
I did not add the members to the channel with channel.addMembers
. I create and add members on the server as this works perfectly for my use case.
If it helps anyone I ended up with these two methods:
public async getChannelUserIds(channelId: string): Promise<string[]> {
try {
const queryChannelsResponse = await this.client.queryChannels(
{ id: channelId },
{ last_updated_at: -1 },
{ message_limit: 0, limit: 1, state: true },
)
const channel = queryChannelsResponse[0]
const userIds = Object.keys(channel.state.members)
console.log('userIds:', userIds)
return userIds
} catch (err) {
throw new InternalServerErrorException(
`Error getting user ids for channel ('${channelId}').`,
err,
)
}
}
public async getOfflineUserIds(userIds: string[]): Promise<string[]> {
try {
const queryUsersResponse = await this.client.queryUsers(
{ id: { $in: userIds } },
{ last_active: -1 },
{},
)
console.log('queryUsersResponse:', queryUsersResponse)
const offlineUserIds = queryUsersResponse.users
.filter(u => !u.online)
.map(u => u.user_id)
return offlineUserIds
} catch (err) {
throw new InternalServerErrorException(
`Error getting offline user ids from ('${JSON.stringify(
userIds,
null,
2,
)}').`,
err,
)
}
}
And then in my webhook I:
@Post('stream/messages')
public async onReceive(
@Req() req: Request,
@Headers('x-signature') signature: string,
@Body() body: any,
) {
try {
console.debug('webhooks-stream.messages.onReceive')
this.chatService.verifyWebhook((req as any).rawBody, signature)
console.log('DEBUG WEBHOOK BODY', JSON.stringify(body, null, 2))
switch (body.type) {
case 'message.new': {
const offlineMemberIds = await this.chatService.getOfflineUserIds(
body.members.map(member => member.user_id),
)
...
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
No vulnerabilities reported
Save this library and start creating your kit
Explore Related Topics
Save this library and start creating your kit