Detect unread messages and display notification on parent website

253 views
Skip to first unread message

Yogeswari Narayasamy

unread,
Dec 3, 2020, 2:15:37 AM12/3/20
to Tinode General
My use case is the chat will be embedded into another website. The user on the parent website will need to be notified when he has an unread message.

I've referred to the question posted here which is to detect unread messages and display a notification accordingly. I'm using ReThinkDB and I've checked that the table 'users' doesn't have a 'new_messages' column.

Seeking some tips to get started on this. Many thanks.



yoges nsamy

unread,
Dec 4, 2020, 5:33:58 AM12/4/20
to tin...@googlegroups.com
Hi Gene, I'd like to follow up on this. Appreciate your help/input on this. Thank you.

--
You received this message because you are subscribed to a topic in the Google Groups "Tinode General" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/tinode/mkC8M655Xzs/unsubscribe.
To unsubscribe from this group and all its topics, send an email to tinode+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/tinode/04dbe201-e190-4595-b821-ccf519f437a0n%40googlegroups.com.

Soy yo

unread,
Dec 4, 2020, 11:43:49 AM12/4/20
to Tinode General
Users table does not contain new mesages column. It's OK when running Tinode as a stand alone messenger but it's a problem if you want to integrate Tinode in another project. Gene already explained how it's possible to count new messages. I didn't tried it yet but I am going to do this with php/grpc - to sub to me and get the number of unread messages for each topic. I guess the most problem will be to show/update/reset the new messages indicator on the parent website more or less in real time.
It's possible to check for new messages every few minutes but at the same time parent website needs to know in some way when user access a Tinode client to stop showing the new messages indicator.
An example. Parent website checks for new messages every 3 minutes and if there are unread messages, shows some indicator. User sees the indicator and access Tinode's webapp to read the messages. After 1 minute when he returns to the parent website, the new messages indicator still will be visible because the next "check" will be done after 2 minutes. I don't know a clean solution for this. The only way - if parent website has detected new messages, to continue sending requests to Tinode on each page reload of parent's website. If there are no new messages anymore, to reset the indicator. But at the same time the user may not want to read new messages at all and parent website will continue sending requests to Tinode on each page reload. 

Gene

unread,
Dec 4, 2020, 12:02:53 PM12/4/20
to Tinode General
On Friday, December 4, 2020 at 2:33:58 AM UTC-8 Yogeswari Narayasamy wrote:
Hi Gene, I'd like to follow up on this. Appreciate your help/input on this. Thank you.

It's difficult for me to comment because I do not understand what kind of problem you are trying to solve. 

Your users gets a list of topics with unread messages when he subscribes to 'me' topic. Why isn't it suitable for you?
You can get browser push notifications when your users gets new messages. 

These are pretty much the same comments as in https://groups.google.com/g/tinode/c/CLIZKNTpGCE/m/Yli30NWPAwAJ

Soy yo

unread,
Mar 11, 2021, 8:04:02 AM3/11/21
to Tinode General
Trying to subscribe to "me" topic using GRPC to get the list of topics but I don't get anything.

I2021/03/11 12:46:00 grpc in: hi:<id:"698950" user_agent:"php/grpc" ver:"1" lang:"es-ES" >  nGn-SnPMEZk
I2021/03/11 12:46:00 grpc in: login:<id:"447598" scheme:"token" secret:"\266\323\3202\336P\365\254\241\010K`\036\000\001\000\001\000Q\023\323<\230'\340\326k\326\316T6e\037x\312=|SJM\241\210\205\310\030!\017Z:\342" > auth_level:ROOT  nGn-SnPMEZk
I2021/03/11 12:46:00 grpc in: sub:<id:"552815" topic:"me" get_query:<what:"sub desc data del cred" > > on_behalf_of:"usrHGshCFWe5aI" auth_level:ROOT  nGn-SnPMEZk

This the only line what I get as response:

{"id":"552815","topic":"me","code":200,"text":"ok"}  


Tried at attach first to the me topic and then to send get request:

 I2021/03/11 12:56:34 grpc: session started g_znfptUpBA XXX.XXX.XXX.XXX:56554 2
I2021/03/11 12:56:34 grpc in: hi:<id:"181951" user_agent:"php/grpc" ver:"1" lang:"es-ES" >  g_znfptUpBA
I2021/03/11 12:56:34 grpc in: login:<id:"944561" scheme:"token" secret:"\266\323\3202\336P\365\254\241\010K`\036\000\001\000\001\000Q\023\323<\230'\340\326k\326\316T6e\037x\312=|SJM\241\210\205\310\030!\017Z:\342" > auth_level:ROOT  g_znfptUpBA
I2021/03/11 12:56:34 grpc in: sub:<id:"265055" topic:"me" > on_behalf_of:"usrHGshCFWe5aI" auth_level:ROOT  g_znfptUpBA
I2021/03/11 12:56:34 grpc in: get:<id:"552653" topic:"me" query:<what:"sub desc data del" > > on_behalf_of:"usrHGshCFWe5aI"  g_znfptUpBA

The same - the response is> {"id":"552815","topic":"me","code":200,"text":"ok"}  but get request does not return absolutely anything.


Tinode CLI:


Tinode command line client. Version 1.5.3/0.16.7; gRPC/1.33.1; Python 2.7.16.
Secure server at 'xxx:6061' SNI=xxx.net
Logging in with login:password xxx:xxx
I2021/03/11 12:36:09 grpc: session started jMMu5eos7v4 XXX.XXX.XXX.XXX:56546 2
I2021/03/11 12:36:09 grpc in: hi:<id:"45583" user_agent:"tn-cli/1.5.3 (Linux/4.19.0-11-amd64); gRPC-python/0.16.7+1.33.1" ver:"0.16.7" lang:"EN" >  jMMu5eos7v4
I2021/03/11 12:36:09 grpc in: login:<id:"45584" scheme:"basic" secret:"xxx:xxx" >  jMMu5eos7v4
=> {"hi": {"lang": "EN", "userAgent": "tn-cli/1.5.3 (Linux/4.19.0-11-amd64); gRPC-python/0.16.7+1.33.1", "ver": "0.16.7", "id": "45583"}}
=> {"login": {"secret": "XXX", "scheme": "basic", "id": "45584"}}
<= {"ctrl": {"text": "created", "code": 201, "params": {"maxTagLength": "OTY=", "minTagLength": "Mg==", "maxFileUploadSize": "ODM4ODYwOA==", "maxMessageSize": "MjYyMTQ0", "build": "Im15c3FsOnYwLjE2LjEwIg==", "maxSubscriberCount": "NDAw", "maxTagCount": "MTY=", "ver": "IjAuMTYi"}, "id": "45583"}}
<= Connected to server: build: mysql:v0.16.10; maxFileUploadSize: 8388608; maxSubscriberCount: 400; maxTagLength: 96; minTagLength: 2; maxMessageSize: 262144; ver: 0.16; maxTagCount: 16
<= 201 created
<= {"ctrl": {"text": "ok", "code": 200, "params": {"token": "<96 bytes: InR0UFFNdDVR...S0VtWFU9Ig==>", "authlvl": "InJvb3Qi", "user": "InVzcnR0UFFNdDVROWF3Ig==", "expires": "IjIwMjEtMDMtMjVUMTI6MzY6MDkuODgyWiI="}, "id": "45584"}}
<= Authenticated as usrttPQMt5Q9aw
<= 200 ok
tn> .use --user HGshCFWe5aI
<= Default user='HGshCFWe5aI'
tn> sub --topic me --get-query desc
I2021/03/11 12:36:50 grpc in: sub:<id:"45586" topic:"me" set_query:<desc:<default_acs:<> > sub:<> > get_query:<what:"desc" > > on_behalf_of:"HGshCFWe5aI"  jMMu5eos7v4
W2021/03/11 12:36:50 s.dispatch: malformed msg.from:  HGshCFWe5aI jMMu5eos7v4
=> {"onBehalfOf": "HGshCFWe5aI", "sub": {"topic": "me", "getQuery": {"what": "desc"}, "setQuery": {"sub": {}, "desc": {"defaultAcs": {}}}, "id": "45586"}}
<= {"ctrl": {"text": "malformed", "code": 400}}
<= 400 malformed


Gene

unread,
Mar 11, 2021, 1:24:05 PM3/11/21
to Tinode General
The user ID is 'usrHGshCFWe5aI', not 'HGshCFWe5aI'.

Soy yo

unread,
Mar 13, 2021, 10:03:44 AM3/13/21
to Tinode General
Yes, you are right, I forgot usr but with GTPC I have it.


Below is a request using Tinode CLI and sending only 1 string - sub

sub --topic me --get-query sub
I2021/03/13 14:30:19 grpc in: sub:<id:"56373" topic:"me" set_query:<desc:<default_acs:<> > sub:<> > get_query:<what:"sub" > > on_behalf_of:"usrHGshCFWe5aI"  e1YD4Qf228s
=> {"onBehalfOf": "usrHGshCFWe5aI", "sub": {"topic": "me", "getQuery": {"what": "sub"}, "setQuery": {"sub": {}, "desc": {"defaultAcs": {}}}, "id": "56373"}}
<= {"ctrl": {"topic": "me", "text": "ok", "code": 200, "id": "56373"}}
<= 200 ok (me)
<= {"meta": {"topic": "me", "id": "56373", "sub": [{"recvId": 1, "readId": 1, "acs": {"given": "JRWPASDO", "want": "JRWPSDO"}, "topic": "grpfJMtErV203Y", "seqId": 1, "updatedAt": "1613057738448", "touchedAt": "1613144276568", "public": "<3052 bytes: eyJmbiI6Imdy...OiJqcGVnIn19>"}, {"acs": {"given": "JRWPASDO", "want": "JRWPSDO"}, "topic": "grp3dr-lX5rx2w", "touchedAt": "1613058773611", "public": "<8804 bytes: eyJmbiI6InNv...InBuZyJ9fQ==>", "updatedAt": "1613058773611"}, {"recvId": 39, "readId": 39, "private": "<72 bytes: eyJjb21tZW50...aSBncnVwYSJ9>", "acs": {"given": "JRWPASDO", "want": "JRWPSDO"}, "topic": "grpi4CSwukyuPQ", "seqId": 39, "updatedAt": "1614676970552", "touchedAt": "1615405797799", "public": "<7888 bytes: eyJmbiI6ImNp...OiJqcGVnIn19>"}, {"recvId": 1, "readId": 1, "private": "eyJjb21tZW50IjoidmFuYWwgYXNhc2FzYXNhc2FzYXMifQ==", "acs": {"given": "JRWPASDO", "want": "JRWPSDO"}, "topic": "grp9E_eeGhIG3g", "seqId": 1, "updatedAt": "1615641902106", "touchedAt": "1615642085028", "public": "<6632 bytes: eyJmbiI6ImNj...ImpwZWcifX0=>", "delId": 1}, {"recvId": 54, "readId": 54, "private": "e30=", "acs": {"given": "JRWPA", "want": "JRWPA"}, "topic": "usraDj1grBOVxw", "seqId": 54, "lastSeenTime": "1611669345000", "updatedAt": "1612177368288", "touchedAt": "1613144560157", "public": "<3148 bytes: eyJmbiI6ImVk...IjoicG5nIn19>", "lastSeenUserAgent": "Cchat/0.16.10 (Chrome/87.0; Win32); tinodejs/0.16.10", "delId": 17}, {"recvId": 4, "readId": 4, "lastSeenTime": "1615644571000", "acs": {"given": "JRWPA", "want": "JRWPA"}, "topic": "usrpDPgde-kKYI", "seqId": 6, "updatedAt": "1615456022457", "touchedAt": "1615463973648", "public": "<3336 bytes: eyJmbiI6Inlv...IjoicG5nIn19>", "lastSeenUserAgent": "Chat/0.16.10 (Chrome/89.0; Linux armv8l); tinodejs/0.16.10"}]}}
<= meta sub me

With GRPC I only get: {"id":"552815","topic":"me","code":200,"text":"ok"}  

Request with GRPC:

I2021/03/13 14:39:06 grpc in: sub:<id:"534379" topic:"me" get_query:<what:"sub" > > on_behalf_of:"usrHGshCFWe5aI" auth_level:ROOT  _S2rGZgaBVA

Reponse with GRPC:

{"id":"528262","topic":"me","code":200,"text":"ok"}    

As I see in the webapp, Tinode returns several messages:

First:

[13:49:47.886] in: {"ctrl":{"id":"108022","topic":"me","code":200,"text":"ok","ts":"2021-03-13T13:49:56.452Z"}} 

and then:

[13:49:47.903] in: {"meta":{"id":"108022","topic":"me","ts":"2021-03-13T13:49:56.452Z","desc":{"updated":"2021-03-11T09:47:02.405Z","touched":"2021-03-.....

Looks that with GRPC I only get the first message and nothing more.

Some time ago I already had a problem that requests like pub, set that need to be attached first, didin't return anything. Then I solved th problem just by removing get_query:<what:"desc" from sub request.





Gene

unread,
Mar 13, 2021, 12:03:32 PM3/13/21
to Tinode General
On Saturday, March 13, 2021 at 7:03:44 AM UTC-8 Soy yo wrote:
Yes, you are right, I forgot usr but with GTPC I have it.


Below is a request using Tinode CLI and sending only 1 string - sub

sub --topic me --get-query sub
I2021/03/13 14:30:19 grpc in: sub:<id:"56373" topic:"me" set_query:<desc:<default_acs:<> > sub:<> > get_query:<what:"sub" > > on_behalf_of:"usrHGshCFWe5aI"  e1YD4Qf228s
=> {"onBehalfOf": "usrHGshCFWe5aI", "sub": {"topic": "me", "getQuery": {"what": "sub"}, "setQuery": {"sub": {}, "desc": {"defaultAcs": {}}}, "id": "56373"}}
<= {"ctrl": {"topic": "me", "text": "ok", "code": 200, "id": "56373"}}
<= 200 ok (me)
<= {"meta": {"topic": "me", "id": "56373", "sub": [{"recvId": 1, "readId": 1, "acs": {"given": "JRWPASDO", "want": "JRWPSDO"}, "topic": "grpfJMtErV203Y", "seqId": 1, "updatedAt": "1613057738448", "touchedAt": "1613144276568", "public": "<3052 bytes: eyJmbiI6Imdy...OiJqcGVnIn19>"}, {"acs": {"given": "JRWPASDO", "want": "JRWPSDO"}, "topic": "grp3dr-lX5rx2w", "touchedAt": "1613058773611", "public": "<8804 bytes: eyJmbiI6InNv...InBuZyJ9fQ==>", "updatedAt": "1613058773611"}, {"recvId": 39, "readId": 39, "private": "<72 bytes: eyJjb21tZW50...aSBncnVwYSJ9>", "acs": {"given": "JRWPASDO", "want": "JRWPSDO"}, "topic": "grpi4CSwukyuPQ", "seqId": 39, "updatedAt": "1614676970552", "touchedAt": "1615405797799", "public": "<7888 bytes: eyJmbiI6ImNp...OiJqcGVnIn19>"}, {"recvId": 1, "readId": 1, "private": "eyJjb21tZW50IjoidmFuYWwgYXNhc2FzYXNhc2FzYXMifQ==", "acs": {"given": "JRWPASDO", "want": "JRWPSDO"}, "topic": "grp9E_eeGhIG3g", "seqId": 1, "updatedAt": "1615641902106", "touchedAt": "1615642085028", "public": "<6632 bytes: eyJmbiI6ImNj...ImpwZWcifX0=>", "delId": 1}, {"recvId": 54, "readId": 54, "private": "e30=", "acs": {"given": "JRWPA", "want": "JRWPA"}, "topic": "usraDj1grBOVxw", "seqId": 54, "lastSeenTime": "1611669345000", "updatedAt": "1612177368288", "touchedAt": "1613144560157", "public": "<3148 bytes: eyJmbiI6ImVk...IjoicG5nIn19>", "lastSeenUserAgent": "Cchat/0.16.10 (Chrome/87.0; Win32); tinodejs/0.16.10", "delId": 17}, {"recvId": 4, "readId": 4, "lastSeenTime": "1615644571000", "acs": {"given": "JRWPA", "want": "JRWPA"}, "topic": "usrpDPgde-kKYI", "seqId": 6, "updatedAt": "1615456022457", "touchedAt": "1615463973648", "public": "<3336 bytes: eyJmbiI6Inlv...IjoicG5nIn19>", "lastSeenUserAgent": "Chat/0.16.10 (Chrome/89.0; Linux armv8l); tinodejs/0.16.10"}]}}
<= meta sub me

I'm not sure where you see the problem. This is your log from tn-cli:

sub --topic me --get-query sub
...
<= 200 ok (me)
...
<= meta sub me

You requested subscriptions and you got subscriptions, exactly as requested. 

With GRPC I only get: {"id":"552815","topic":"me","code":200,"text":"ok"}  

tn-cli is gRPC. You have a bug in your code.

Soy yo

unread,
Mar 13, 2021, 1:19:37 PM3/13/21
to Tinode General
Yes, but I was expecting that Tinode will return subscriptions, not just the status of the request.
But response  {"id":"552815","topic":"me","code":200,"text":"ok"}   is pointless. I only know that sub request was processed correctly, but I don't get any topic information.

I generated php classes from Tinode's proto file and I can do anything - create/delete topics, subscribe/unsubscribe users, send messages but can't get data of the me topic that I need to check unread messages count.  

Probably I do not understand something in GRCP. Does it return only status of the request?

As I wrote before, I also tried first to send sub without requesting any data and then get request.

Sub request returns code 200 and text ok but get request does not return anything. In case of a bug in my code, there should appear some error message or php script should stop executing. No error messages, not in Tinode, nor php, nothing.

If get request has some problem, the script should stop excecuting or Tinode should return some error, but nothing. To make sure that the sript continues running after get request, I added hi request after that. It works and returns response.

Php:

$behalf = 'usr'.$user.'';
define('C_BEHALF', $behalf);

$response1 = $service->attach_me(random_int(100000, 999999),'me');
$response2 = $service->get(random_int(100000, 999999), 'me');
$response3 = $service->hi(random_int(100000, 999999), $user_agent, $ver, $lang);

echo 'Response1: '.$response1.'<br />'; 
echo 'Response2: '.$response2.'<br />';
echo 'Response3: '.$response3.'<br />';

Response: 

Response1: {"id":"518561","topic":"me","code":200,"text":"ok"}
Response2:
Response3: {"id":"106926","code":201,"text":"created"} 

As you can see, $response2 is empty. 

Server log:

I2021/03/13 18:01:31 grpc in: sub:<id:"518561" topic:"me" > on_behalf_of:"usrHGshCFWe5aI" auth_level:ROOT  yEq9m_81aR4
I2021/03/13 18:01:31 grpc in: get:<id:"344457" topic:"me" query:<what:"sub" > > on_behalf_of:"usrHGshCFWe5aI"  yEq9m_81aR4
I2021/03/13 18:01:31 grpc in: hi:<id:"106926" user_agent:"php/grpc" ver:"1" lang:"es-ES" >  yEq9m_81aR4

Is there something wrong with get request? I am really getting crazy with this. Thanks.




Gene

unread,
Mar 13, 2021, 1:40:29 PM3/13/21
to Tinode General
On Saturday, March 13, 2021 at 10:19:37 AM UTC-8 Soy yo wrote:
Yes, but I was expecting that Tinode will return subscriptions, not just the status of the request.

It does return subscriptions. You can see it clearly in tn-cli. You are not seeing the subscriptions in your code because you have a bug in your code.

Gene Sokolov

unread,
Mar 13, 2021, 2:27:31 PM3/13/21
to tin...@googlegroups.com
If you are developing an open source project, please point me to your public repo and I'll take a look at your code.

You received this message because you are subscribed to the Google Groups "Tinode General" group.
To unsubscribe from this group and stop receiving emails from it, send an email to tinode+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/tinode/b7a184cd-c04c-419d-bd9f-84a7b3a3e4bdn%40googlegroups.com.

Soy yo

unread,
Mar 15, 2021, 8:35:54 AM3/15/21
to Tinode General
It's not an open source project. I am just trying to integrate Tinode in my existing project. Almost everything is done already, except I can't get the number of unread messages of users. But it very important.

Probably the problem is the following. After each GRPC request I see the following message on the server:

E2021/03/15 10:23:35 grpc: recv sxNwJc_UgJ0 rpc error: code = Canceled desc = context canceled

I have googled for it but didn't find any solution. Generally, this error does not affect anything and I get responses from the server. There are problems only with sub and get requests. sub request returns just one message {"id":"552815","topic":"me","code":200,"text":"ok"} and nothing more. Probably it's because of this error and connection closes.

But it doesn't explain why I don't get any response to the get request because get request should return only one message but I don't get anything. Yes, it returns but only in a case of invalid request. For example, if I send a get request without what, Tinode returns error:

Server log:
I2021/03/15 10:39:49 grpc in: get:<id:"975419" topic:"me" > on_behalf_of:"usrHGshCFWe5aI"  BhWWZOFpWvQ
W2021/03/15 10:39:49 s.get: invalid Get message action
E2021/03/15 10:39:49 grpc: recv BhWWZOFpWvQ rpc error: code = Canceled desc = context canceled

Response:
{"id":"975419","topic":"me","code":400,"text":"malformed"}  

However, if I send a valid request, Tinode does not return absolutely anything and there are no errors on the server:

Server log:
I2021/03/15 10:46:01 grpc in: get:<id:"315201" topic:"me" query:<what:"sub" > > on_behalf_of:"usrHGshCFWe5aI"  OF6yIcVT8Ck
E2021/03/15 10:46:01 grpc: recv OF6yIcVT8Ck rpc error: code = Canceled desc = context canceled

Response
empty

I also can not understand some things in the documentation about get request. It says that what should not be a part of of sub:

get: {
  id: "1a2b3", // string, client-provided message id, optional
  topic: "grp1XUtEhjv6HND", // string, name of topic to request data from
//why to inslude "topic" twice?
  what: "sub desc data del cred", // string, space-separated list of parameters to query;

.....
.....

  // Optional parameters for {get what="sub"}
  sub: {
    ims: "2015-10-06T18:07:30.038Z", // timestamp, "if modified since" - return
          // public and private values only if at least one of them has been
          // updated after the stated timestamp, optional
    user: "usr2il9suCbuko", // string, return results for a single user,
                          // any topic other than 'me', optional
    topic: "usr2il9suCbuko", // string, return results for a single topic,
                           // 'me' topic only, optional
//why to inslude "topic" twice?  If only for "me"topic, why the value in the example is  "usr2il9suCbuko" and not "me"?
    limit: 20 // integer, limit the number of returned objects
  },
  .....
  .....
.......

So, according to the documentation, get request should look like this:

I2021/03/15 11:13:00 grpc in: get:<id:"877216" topic:"me" what:"sub" > > on_behalf_of:"usrHGshCFWe5aI"  Q8SHxyIjcjo

but as I wrote, I automatically generated php classes from Tinode's proto file and according to the generated classes what should be a part of sub

I2021/03/15 11:13:00 grpc in: get:<id:"877216" topic:"me" query:<what:"sub" > > on_behalf_of:"usrHGshCFWe5aI"  Q8SHxyIjcjo

I don't know, probably there is some error in proto file. All the php classes that need to send what contains 2 functions: getWhat(); and setWhat(); Class ClientGet does not contain it and that's why I underestand that what should be a part of sub.

How exactly should look get request to get the number of unread messages for an user?

Is there another way to get the number of unread messages for a user? I could query directly the database but it does not contain real values in  recvseqid and readseqid. As I understand the data updates from time to time and the values are not real.

Suggestion: please make a new client to server message, something like:

unread: {
id: "1a2b3", 
topic: "me",
user: "usr..."
}

with response something like this:

{"id":" 1a2b3  ","unread":17}  

that only will return the number of unread messages. Even if --get-query sub would work with GRPC, it returns a huge amount of useless data that needs to be processed just to get one number - the amount of unread messages. As I see in the forums, I am not the only person who needs it. And it's a very important thing to integrate Tinode in an existing project. 

Gene

unread,
Mar 15, 2021, 11:41:58 AM3/15/21
to Tinode General
On Monday, March 15, 2021 at 5:35:54 AM UTC-7 Soy yo wrote:
It's not an open source project. I am just trying to integrate Tinode in my existing project. Almost everything is done already, except I can't get the number of unread messages of users. But it very important.

Probably the problem is the following. After each GRPC request I see the following message on the server:

E2021/03/15 10:23:35 grpc: recv sxNwJc_UgJ0 rpc error: code = Canceled desc = context canceled

You are probably not using a bi-directional streaming gRPC:

Or closing the connection after receiving the first response.
Reply all
Reply to author
Forward
0 new messages