I am trying to use Telethon with AWS Lambda. More precisely I am trying get messages from some public channels using client object.
Is there a way to import an existing session in AWS Lambda, in order to prevent Telegram/telethon to ask for a validation code (which is not possible to input) ?
Here is the code I am using to try to connect to telegram through telethon in AWS Lambda :
api_id== os.environ.get('TELEGRAM_API_ID')
api_hash = os.environ.get('TELEGRAM_API_HASH')
userName = os.environ.get('TELEGRAM_USERNAME')
phone = os.environ.get('TELEGRAM_PHONE')
os.chdir("/tmp")
client = TelegramClient(userName, api_id, api_hash)
Here is the session file I have imported in AWS Lambda through Layers (same name as userName) session file
But it seems the session file is not used/read as telethon is asking the verification code and phone number.
Anyone know how to fix this ? Thanks
It took some time, but I found a solution to this problem and ran a Telegram client on Lambda)
All you need to do is use a different session type, namely StringSession.
As indicated in the official documentation, all you need to do is generate a StringSession in your local environment, save the string in a file or local variables and use it in your lambda code.
Generate StringSession, you will see the output in your terminal in this case:
from telethon.sync import TelegramClient
from telethon.sessions import StringSession
with TelegramClient(StringSession(), api_id, api_hash) as client:
print(client.session.save())
Save your newly created StringSession into environment variables in Lambda, as described here and now you can do something like this:
from telethon.sync import TelegramClient
from telethon.sessions import StringSession
import os
string = os.environ.get('session') # env variable named "session"
with TelegramClient(StringSession(string), api_id, api_hash) as client:
client.loop.run_until_complete(client.send_message('me', 'Hi'))
Related
I'm trying to get the Session String from an existing Session File of Pyrogram. How can I do that?
Can you help me?
from dotenv import dotenv_values
from pyrogram import Client
config = dotenv_values(dotenv_path='./.env')
app = Client(
# name="withstring",
name="my_bot",
# api_id=config.get("API_ID"),
# api_hash=config.get("API_HASH"),
bot_token=config.get("BOT_TOKEN"),
)
with app:
app.send_message("username", text="Hello world Minhaz!")
s = app.export_session_string()
# print(s)
app.run()
The Session File is an sqlite database storing your authorization against the API and peers you've met (messages received, chats joined, etc).
To get the Session String to authenticate in Memory (losing peers when you log in again), you can just call the Client.export_session_string() method.
Edit to add: If you already have a session file, you can use its name to log in, instead of creating a new in-memory session. If you have a my_account.session file, use Client("my_session") when instantiating your Client.
from pyrogram import Client
app = Client(":memory:")
with app:
session = app.export_session_string()
print(session)
import discord
import asyncio
from discord.ext import commands
client = commands.Bot(command_prefix=':')
token = ''
#client.event
async def on_ready():
print('BOT ONLINE')
#client.event
async def on_message(message):
channel = message.channel
if message.content.startswith('/'):
if message.content.startswith("/users"):
# FOR LOOP IN QUESTION ---------------
for guild in client.guilds:
for member in guild.members:
print(member) # or do whatever you wish with the member detail
client.run(token)
print("Bot Finished")
When I run this code all it returns is the bot name twice. The server has two members, myself and the bot. I need to iterate through every member of the server. What am I doing wrong?
You simply didn't enable intents.members
intents = discord.Intents.default()
intents.members = True
client = commands.Bot(command_prefix=":", intents=intents)
Also make sure to enable them in the developer portal
Reference:
intents.members
How to enable privileged intents
Im trying to create a fortnite API to give me all of the Leaked cosmetics with a discord bot and I just don't know where to get started! Could someone help! Thankyou
Here's an example of a simple bot that repeats what you say.
import discord # importing the discord.py library
from discord.ext import commands # extension of said library
# This defines the bot's command prefix and if the commands are case insensitive
bot = commands.Bot(command_prefix='-', case_insensitive='True')
#bot.event =
async def on_ready():
```
This is an event that prints to the console that the bot is online and ready to go.
```
print('Bot is ready!') # prints to console that the bot is ready.
#bot.command()
async def echo(ctx, *, msg):
```
This is a command that repeats what you say. Echo is the name. *, msg means that all the words listed after the command are repeated
```
await ctx.message.delete() # delete the command message the the user said
await ctx.send(msg) # say whatever the user wanted to say.
# bot token here. found in the discord developer website
bot.run(YOUR BOT'S TOKEN GOES HERE)
Here's an example of using a api inside a Cog
Necessary Imports:
from discord.ext import commands
from discord import Embed, Color
from aiohttp import ClientSession
from ast import literal_eval
A command to fetch random Chuck Norris jokes
#commands.command()
async def chuck(self, ctx):
ad = Embed(color=Color.dark_gold())
base = "https://api.chucknorris.io/jokes/random"
async with ClientSession() as session:
data = await get(session, base)
data = literal_eval(data)
ad.set_author(name="Chuck Norris",
icon_url="https://i.ibb.co/swZqcK7/norris.gif",
url=data['url'])
ad.description = data['value']
return await ctx.send(embed=ad)
If you're getting information from Fortnite, chances are they already have a Python Module on PyPi, alternatively you can look for a JSON endpoint and apply what I did above to get what you need.
I would like to access the docker API using the /var/lib/docker.sock unix domain socket. I've seen examples where you can use (modern versions of) curl to call the API as follows:
curl --unix-socket /var/run/docker.sock http:/containers/json
where the REST command is expressed in the /containers/json path. I was excited to see the Alpakka Unix Domain Socket adapter, but you only seem to be able to send and receive raw bytes. Is there any elegant way to do this? Or do I have to manually construct an HTTP header and manage all the difficult stuff manually?
Here's a working snippet (see also the rest of the discussion at akka/akka-http#2139):
build.sbt:
val scalaV = "2.12.6"
val akkaV = "2.5.14"
val akkaHttpV = "10.1.3"
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-http" % akkaHttpV,
"com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpV,
"com.typesafe.akka" %% "akka-stream" % akkaV,
"com.lightbend.akka" %% "akka-stream-alpakka-unix-domain-socket" % "0.20",
)
DockerSockMain.scala:
import java.io.File
import java.net.InetSocketAddress
import akka.actor.ActorSystem
import akka.http.scaladsl.ClientTransport
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.model.HttpRequest
import akka.http.scaladsl.model.HttpResponse
import akka.http.scaladsl.settings.ClientConnectionSettings
import akka.http.scaladsl.settings.ConnectionPoolSettings
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream.ActorMaterializer
import akka.stream.alpakka.unixdomainsocket.scaladsl.UnixDomainSocket
import akka.stream.scaladsl.Flow
import akka.util.ByteString
import spray.json.JsValue
import scala.concurrent.Future
object DockerSockMain extends App {
object DockerSockTransport extends ClientTransport {
override def connectTo(host: String, port: Int, settings: ClientConnectionSettings)(implicit system: ActorSystem): Flow[ByteString, ByteString, Future[Http.OutgoingConnection]] = {
// ignore everything for now
UnixDomainSocket().outgoingConnection(new File("/var/run/docker.sock"))
.mapMaterializedValue { _ =>
// Seems that the UnixDomainSocket.OutgoingConnection is never completed? It works anyway if we just assume it is completed
// instantly
Future.successful(Http.OutgoingConnection(InetSocketAddress.createUnresolved(host, port), InetSocketAddress.createUnresolved(host, port)))
}
}
}
implicit val system = ActorSystem()
implicit val mat = ActorMaterializer()
import system.dispatcher
val settings = ConnectionPoolSettings(system).withTransport(DockerSockTransport)
import SprayJsonSupport._
def handleResponse(response: HttpResponse): Future[String] =
// TODO: create docker json model classes and directly marshal to them
Unmarshal(response).to[JsValue].map(_.prettyPrint)
Http().singleRequest(HttpRequest(uri = "http://localhost/images/json"), settings = settings)
.flatMap(handleResponse)
.onComplete { res =>
println(s"Got result: [$res]")
system.terminate()
}
}
Interesting use-case. You should be able to use Alpakka Unix Domain socket flow and put Akka Http ClientLayer on top of it.
The short answer to the question is "It Can't be Done"—at least not with the existing building blocks of Akka HTTP and the Alkappa Unix Domain Sockets. You would have to handle writing the HTTP GET request by manually sending the headers, i.e. (using the Docker API as an example)
GET /v1.24/containers/json HTTP/1.1\n
Host: localhost\n
\n\n
...and then reading the TCP response manually. Additionally, the Unix Domain Socket logic can't use the Alpakka code because it only currently provides a ServerBinding, and thus is designed to create a server that handles requests to a Unix socket, not to send data to a Unix socket and handle the response.
So everything has to be done manually. There's another StackOverflow question here that points out how the AFUNIXSocket github source code could be used to help with some of the low-level Unix Domain Socket logic that might be of help to others wanting to tackle this same problem.
The most elegant solution would also involve (as suggested by dvim's comment) writing an HTTP.ClientTransport to plug in the Unix Domain Socket communication layer and allow the HTTP library to expose the low-level functionality of writing request/response headers, etc. (One interesting note on that is that the API assumes a host/port parameter pair, which is tightly bound to the TCP paradigm.)
When I had my hard-coded access key and secret key present, I was able to generate authenticated urls for users to view private files on S3. This was done with the following code:
import hmac
import sha
import urllib
import time
filename = "".join([s3_url_prefix, filename])
expiry = str(int(time.time()) + 3600)
h = hmac.new(
current_app.config.get("S3_SECRET_KEY", None),
"".join(["GET\n\n\n", expiry, "\n", filename]),
sha
)
signature = urllib.quote_plus(base64.encodestring(h.digest()).strip())
return "".join([
"https://s3.amazonaws.com",
filename,
"?AWSAccessKeyId=",
current_app.config.get("S3_ACCESS_KEY", None),
"&Expires=",
expiry,
"&Signature=",
signature
])
Which gave me something to the effect of https://s3.amazonaws.com/bucket_name/path_to_file?AWSAccessKeyId=xxxxx&Expires=5555555555&Signature=eBFeN32eBb2MwxKk4nhGR1UPhk%3D. Unfortunately, I am unable to store keys in config files for security reasons. For this reason, I switched over to IAM roles. Now, I get my keys using:
_iam = boto.connect_iam()
S3_ACCESS_KEY = _iam.access_key
S3_SECRET_KEY = _iam.secret_key
However, this gives me the error "The AWS Access Key Id you provided does not exist in our records.". From my research, I understand that this is because my IAM keys aren't the actual keys, but instead used with a token. My question therefore, is twofold:
How do I get the token programatically? It doesn't seem that there is a simple iam property I can use.
How do I send the token in the signature? I believe my signature should end up looking something like "".join(["GET\n\n\n", expiry, "\n", token, filename]), but I can't figure out what format to use.
Any help would be greatly appreciated.
There was a change to the generate_url method in https://github.com/boto/boto/commit/99e14f3df039997f54a5377cb6aecc83f22c2670 (June 2012) that made it possible to sign a URL using session credentials. That means you would need to be using a boto version 2.6.0 or later. If you are, you should be able to just do this:
import boto
s3 = boto.connect_s3()
url = s3.generate_url(expires_in=3600, method='GET', bucket='<bucket_name>', key='<key_name>')
What version are you using?