Associate thread with object

For context:
I am currently building a notification service with rust. The idea is to have a service that you can POST a request to that send a notification to a set of notification services such as email, web push notifications or telegram. In the telegram service I need to solve the problem that in the telegram you cannot send a message to a username, but only to a chat id. In order to resolve a username to a chat id you need to send a message to the bot and the bot needs regularly poll incoming messages and look for the username.

This makes it a bit difficult to handle in my current model in rust: Each notification service client is a struct that implements a trait forcing it to have a send method. The telegram service has another method that is intended to resolve a username to a chat id. To do so it must poll for incoming notifications until the right username is found. However this polling may not happen in each concurrent method call since this would waist resources and potentially skip messages corresponding to other method calls. Therefore my idea was to associate a thread polling messages with the client object and have a central list of lookup requests that is checked in each poll. In principle this works. However the lookup thread needs to be stopped when the client is dropped and started again when the client is stopped. All in all this seems far too complicated.

What's the nicest way to solve such problems in rust? Do you maybe even know a solution to the exact issue I am trying to solve?
My code is here and the following is the service I am talking about:

use std::{collections::HashMap, time};

use super::service;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};

use teloxide::{payloads, prelude::*, requests, types::ChatId};
use tokio::{
        broadcast::{channel, Sender},
pub struct TelegramService {
    bot: AutoSend<Bot>,
    request_sender: Sender<(String, mpsc::Sender<ChatId>)>,
    stop_request_sender: Sender<String>,
    stop_sender: Sender<()>,

#[derive(Serialize, Deserialize, Debug)]
struct TelegramConfig {
    token: String,
impl service::Service for TelegramService {
    async fn send(&self, receiver: String, subject: String, message: String) {
        println!("Sending message to {}: {}", receiver, message);
                format!("{}\n{}", subject, message),

impl TelegramService {
    pub async fn get_chat_id(&self, username: String) -> Result<ChatId, String> {
        //let (tx, rx) = mpsc::channel(10);
        let (sender, mut receiver) = mpsc::channel(10);
            .send((username.clone(), sender))
        match tokio::time::timeout(time::Duration::from_secs(60), receiver.recv()).await {
            Ok(a) => {
                return Ok(a.unwrap());
            Err(_) => {
                return Err("timeout".to_string());
    pub fn load(serialized: String) -> Self {
        let config: TelegramConfig = serde_json::from_str(&serialized).unwrap();
        let raw_bot = Bot::new(config.token);
        let bot = raw_bot.clone().auto_send();
        let (request_sender, mut request_receiver) = channel(10);
        let (stop_request_sender, mut stop_request_receiver) = channel(10);
        let (stop_sender, mut stop_receiver) = channel(10);
        //let update_bot = bot.clone();
        //start a thread to handle username lookups
        tokio::spawn(async move {
            let mut offset = 0;
            let mut username_lookups: HashMap<String, mpsc::Sender<ChatId>> = HashMap::new();
            let mut interval = tokio::time::interval(time::Duration::from_secs(3));
            loop {
                select! {
                    req=request_receiver.recv() => {
                        match req{
                            Ok((username, sender))=>{
                                username_lookups.insert(username, sender);
                            Err(e)=>print!("Error receiving username lookup request: {}", e)
                        if username_lookups.len()==0{
                        //check for messages here
                        let updates=requests::JsonRequest::new(raw_bot.clone(),payloads::GetUpdates::new().offset(offset)).send().await.unwrap();
                        //let updates = update_bot.get_updates().await.unwrap();
                        for update in updates.iter() {
                            let chat =;
                            let username = chat.username().unwrap();
                            match username_lookups
                                Some(sender) => {
                                    println!("Found user: {}",;
                                None => {}

                        let username = username.unwrap();
                    _=stop_receiver.recv() => {
                        println!("Stopping username lookup thread");
                        return ;
        TelegramService {
            bot: bot,
            request_sender: request_sender,
            stop_request_sender: stop_request_sender,
            stop_sender: stop_sender,
    pub const ID: &'static str="telegram";
impl Drop for TelegramService {
    fn drop(&mut self) {
        if let Err(e)=self.stop_sender.send(()){

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.