1

I'm trying to deploy a python telethon web application (with quart) and when i test locally, telethon can connect to Telegram account without any problem but when i test in a docker container, the connection fails.

Here are the logs:

enter image description here

(and it get stuck there)

The dockerfile:

# Use the official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.9-slim

# Allow statements and log messages to immediately appear in the Knative logs
ENV PYTHONUNBUFFERED True

# Copy local code to the container image.
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./

COPY sessionname.session ./sessionname.session

# Install production dependencies.
RUN pip install --no-cache-dir -r requirements.txt

# Run the web service on container startup. Here we use the gunicorn
# webserver, with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
# Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
CMD exec gunicorn --worker-class uvicorn.workers.UvicornWorker --bind :$PORT --workers 1 --preload --threads 8 --timeout 1500 main:app

and my main.py file

import base64
import os
import json
import hypercorn.asyncio
import logging

from quart import jsonify, Quart, render_template_string, request
from telethon import TelegramClient, utils
from telethon.tl.types import InputChannel, InputPeerChannel, InputPhoneContact, InputPeerUser
from telethon.tl.functions.contacts import ImportContactsRequest
from telethon.errors import SessionPasswordNeededError
from telethon.tl.functions.messages import ExportChatInviteRequest, SendMessageRequest
from telethon.tl.functions.channels import CreateChannelRequest, CheckUsernameRequest, UpdateUsernameRequest, SetDiscussionGroupRequest, ToggleSignaturesRequest, InviteToChannelRequest
from requests.models import Response

logging.basicConfig(level=logging.DEBUG)

api_id = xxxx
api_hash = 'xxxxx'

#phone numberI
phone_number = '+xxxx'

print("create and connect to the telegram client")

client = TelegramClient('sessionname', api_id, api_hash)

app = Quart(__name__)

# Connect the client before we start serving with Quart
@app.before_serving
async def startup():
    print("run before_serving!! (waiting for connection)")
    try:
        await client.start()
    except:
        print ("Could not connect to telegram")

    print("connect is OK!!")

# After we're done serving (near shutdown), clean up the client
@app.after_serving
async def cleanup():
    print("after serving ok!")
    await client.disconnect()

@app.route('/check', methods=['POST'])
async def sayHello():
    return "hola"

@app.route('/channel/create', methods=['POST'])
async def createChannel():
    
    strBody = await request.get_data()

    parsedBody = json.loads(strBody)

    # Create the channel
    createdPrivateChannel = await client(CreateChannelRequest(parsedBody["title"], parsedBody["about"], megagroup=False))

    # Create the supergroup for the comments
    discussionSuperGroup = await client(CreateChannelRequest(parsedBody["title"], parsedBody["about"], megagroup=True))

    await client(SetDiscussionGroupRequest(createdPrivateChannel.__dict__["chats"][0].__dict__["id"], discussionSuperGroup.__dict__["chats"][0].__dict__["id"]))

    # Send welcome message to the channel
    welcomeMessageResult = await client(SendMessageRequest(
        createdPrivateChannel.__dict__["chats"][0].__dict__["id"],
        'Bienvenidx',
        True
    ))

    # Enable signature in messages
    toggleSignatureResult = await client(ToggleSignaturesRequest(
        createdPrivateChannel.__dict__["chats"][0].__dict__["id"],
        True
    ))

    # # Invite the bots to the channel
    # inviteToChannelResult = await client(InviteToChannelRequest(
    #     createdPrivateChannel.__dict__["chats"][0].__dict__["id"],
    #     ['userIdOfTheBot']
    # ))

    # Create an invitation link
    channelInviteLink = await client(ExportChatInviteRequest(
        createdPrivateChannel.__dict__["chats"][0].__dict__["id"]
    ))

    response = jsonify({
        "channel_id": createdPrivateChannel.__dict__["chats"][0].__dict__["id"], 
        "title": parsedBody["title"],
        "about": parsedBody["about"],
        "chat_invite_link": channelInviteLink.__dict__["link"]
    })

    return response


async def main():
    quart_cfg = hypercorn.Config()
    quart_cfg.bind = ["0.0.0.0:8888"]
    await hypercorn.asyncio.serve(app, quart_cfg)

if __name__ == '__main__':
    client.loop.run_until_complete(main())

can you help me? I don't understan why locally the connection works fine but in docker container not!

thanks!!

Alex
  • 207
  • 2
  • 3
  • 11
  • I'd recommend trying to get Telethon working inside Docker without `quart`, and once that's working, start using `quart`. If you're able to [create a Minimal, Complete, Verifiable Example](https://stackoverflow.com/help/minimal-reproducible-example) with just Telethon and Docker that exhibits this issue, you may want to open a bug report with it. – Lonami Sep 24 '21 at 14:47
  • Hi @Lonami thanks for you answer. I change the Dockerfile EXEC command from: CMD exec gunicorn --worker-class uvicorn.workers.UvicornWorker --bind :$PORT --workers 1 --preload --threads 8 --timeout 0 main:app TO: CMD ["python", "main.py"] and right now the code works perfectly! The problem is directly when i try to run it with Gunicorn (i have also tested with Hypercorn an the same it happened) Can you get an idea why gunicorn doesn't work? I'm using gunicorn following the docs of google cloud run: https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python – Alex Sep 24 '21 at 20:33
  • No, sorry, I'm not experienced in Gunicorn. You may try [hypercon](https://github.com/LonamiWebs/Telethon/blob/2e1be01ad4f6462de2e9e1f96a33537e51f44980/telethon_examples/quart_login.py#L132-L133). – Lonami Sep 25 '21 at 09:54

0 Answers0