使用tweepy与discord.py将推文发布到特定频道

问题描述 投票:2回答:1

因此,我试图通过结合使用教程,文档和在线示例来学习一些Python,以构建一个Discord机器人,该机器人实现了一个或多个现有机器人的某些功能。这些功能之一是将来自一组特定Twitter帐户的(新)推文发布到我的Discord服务器上的特定频道。我已经找到了几段零碎的代码,这些代码已经拼凑在一起,可以从Twitter流中读取,并在这里和那里“调整”一些东西以尝试实现此目的。

但是,我一直无法正确执行on_status代码。我尝试了多种方法使其正常运行,但是我尝试过的所有方法都会导致某种错误。以下是我测试过的最新迭代中的相关代码(已编辑):

import discord
import tweepy

from discord.ext import commands
from tweepy import Stream
from tweepy.streaming import StreamListener

class TweetListener(StreamListener):
    def on_status(self, status):
        if status.in_reply_to_status_id is None:
            TweetText = status.text

            for DGuild in MyBot.guilds:
                for DChannel in DGuild.text_channels:
                    if DChannel.name == 'testing':
                        TwitterEmbed = discord.Embed(title='New Tweet', description='New Tweet from my timeline.', color=0xFF0000)
                        TwitterEmbed.set_author(name='@TwitterHandle', icon_url=bot.user.default_avatar_url)
                        DChannel.send(TweetText, embed = TwitterEmbed)

DISCORD_TOKEN = 'dtoken'
TWITTER_CONSUMER_KEY = 'ckey'
TWITTER_CONSUMER_SECRET = 'csecret'
TWITTER_ACCESS_TOKEN = 'atoken'
TWITTER_ACCESS_SECRET = 'asecret'

MyBot = commands.Bot(command_prefix='!', description='This is a testing bot')
TwitterAuth = tweepy.OAuthHandler(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET)
TwitterAuth.set_access_token(TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_SECRET)
TweetAPI = tweepy.API(TwitterAuth)
NewListener = TweetListener()
NewStream = tweepy.Stream(auth=TweetAPI.auth, listener=NewListener)
NewStream.filter(follow=['USER IDS HERE'], is_async=True)

@bot.event
async def on_ready():
    print(MyBot.user.name,'has successfully logged in ('+str(MyBot.user.id)+')')
    print('Ready to respond')

MyBot.run(DISCORD_TOKEN)

当我在测试帐户的时间轴上发布一条推文时,[channel.send()方法生成以下消息:

RuntimeWarning: coroutine 'Messageable.send' was never awaited

我了解消息-channel.send()方法是异步方法,但是on_status()事件处理程序是同步方法,我无法在await channel.send()内部使用on_status()-但我不知道如何让它起作用。我试图使on_status()方法成为异步方法:

    async def on_status(self, status):
        <same code for checking the tweet and finding the channel>
        await DChannel.send(TweetText, embed = TwitterEmbed)

但是这总是导致类似的警告:

RuntimeWarning: coroutine 'TweetListener.on_status' was never awaited
if self.on_status(status) is False:

[我找到了问题,[[How do I async on_status from tweepy?,并跟随了那里的链接,但是我没有见导致我陷入编码错误的任何内容。

通过其他研究,我还尝试使用来自asyncio库的一些调用来进行调用:

#channel.send(message, embed = TwitterEmbed) #---ASYNCIO TEST--- loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.gather(DChannel.send(embed=TwitterEmbed))) loop.close()

[不幸的是,当它尝试执行指示loop=asyncio.get_event_loop()There is no current event loop in thread 'Thread-6'.时,这会导致未处理的异常(因此,即使我无法使用

that

也无法使用run_until_complete()方法)我目前还不太乐观。我意识到已经有像MEE6这样的现有Discord机器人已经可以完成我在这里想要做的事情,但是我希望能够在让我逐渐了解Python的同时让这个机器人成为“我自己的机器人”。我可能在这里忽略了一些简单的事情,但是我无法在API文档中找到tweepydiscord.py的任何东西,这些东西似乎都在指出正确的方向,而我的

Google-fu

显然没有那么强。到目前为止,我能够找到的所有示例似乎都是过时的,因为它们似乎都引用了不推荐使用的方法以及其中一个或两个库的较旧版本。我还有其他事情要弄清楚该怎么做(例如,获取@TwitterHandle以正确填充嵌入内容),但是我必须使它能够读取并推送推文在我担心到达这一点之前。
环境信息

Visual Studio 2017 CE

Python 3.6

discord.py 1.3.3

tweepy 3.8.0


更新:附加测试

所以,为了向自己证明我的TweetListener类实际上正在工作,我继续并注释了on_status方法中与Discord的所有交互(

for Guild in MyBot.guilds到结束的所有内容方法

),然后添加一个简单的print(TweetText)。我再次运行代码,登录到该项目的测试Twitter帐户,并发布了更新。检查控制台,它按我的预期正确输出了我的推文的status.text。正如我所想,我在这里遇到的唯一问题是尝试将send文本发送给Discord。我还尝试以同步模式而不是异步方式初始化tweepy.Stream.filter-NewStream.filter(follow=['USER IDS'])。这只会导致我的应用程序在启动时基本上“挂起”。我意识到这是由于其在代码中的位置,因此我将TweetListener初始化的所有三行都移到了on_ready事件方法。我将Discord代码注释掉并进行了测试,然后再次运行,因为它在控制台上打印了另一条测试推文的status.text。但是,该漫游器不会对其他任何响应(我认为仍然在流中“等待”)。

即使如此,只是为了看看是否会有任何不同,我继续并没有注释Discord交互代码,然后再次尝试。结果与我最初的结果相同,只是这次Discord机器人不会响应通道中的活动(

代码中其他地方有几个超简单的@bot.command声明

),这再次基本上会带我回到相同问题的原始代码。我敢肯定,有更好的方法来编码整个内容。就像我在上面说的,我现在才开始使用Python并仍然学习语法规则等。即使这样,我仍然不确定自己在做错什么,并且/或者忽略了这样做是否妨碍了我实现我原本认为是相当简单的任务。


更多测试

我回去发现了更多的asyncio示例,并在on_status中的Discord交互中尝试了另一个迭代:

loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) result=loop.run_until_complete(DChannel.send(embed=TwitterEmbed))

这次,我得到了一个比以前更未处理的

different

异常。当它收到一条新推文时,在run_until_complete行上发生错误:Timeout context manager should be used inside a task
[我考虑了这一进展,但是显然我还没有到位,所以回到Google时,我发现了asgiref库及其sync_to_asyncasync_to_sync方法的信息,所以我想我会给出尝试。

asgiref.sync.async_to_sync(DChannel.send)(embed=TwitterEmbed)

[不幸的是,我得到了与asyncio版本相同的未处理异常。我觉得我马上就知道了,但这还没有被“点击”。


另一回合

[好,因此,在查找有关我要超越的Timeout context manager异常的信息之后,我偶然发现了另一个SO问题,给了我一线希望。关于RuntimeError: Timeout context manager should be used inside a task的答案再次使我回到使用asyncio,并在使用asyncio.run_coroutine_threadsafe方法提供有用建议之前,对OP问题背后的原因进行了简短但描述性的解释。查看推荐的代码,这可以帮助我有效地启动sync-> async方法通信。我实现了建议的更改(为要在其上运行bot的Thread对象创建了一个全局变量,添加了在该循环中生成bot的“启动”方法,然后更改了on_status中的Discord交互以将其全部在一起。

“好消息”是,当我发布推文时,我不再遇到

any

错误。另外,测试bot命令似乎也可以正常工作。坏消息是它仍然没有将消息发送到频道。由于它不会产生任何错误,因此无法确定消息在何处结束。
python twitter discord discord.py tweepy
1个回答
1
投票
我经历了与您相同的问题,并从Google获得了确切的链接并尝试了所有链接,但正如您所提到的,没有任何作用。

因此,在我经过大量尝试和修补后,我可以将主循环传递给tweepy侦听器,并使用run_coroutine_threadsafe执行异步功能。

这是我的代码的要点:

听众:

class EpicListener(tweepy.StreamListener): def __init__(self, discord, loop, *args, **kwargs): super().__init__(*args, **kwargs) self.discord = discord # this is just a function which sends a message to a channel self.loop = loop # this is the loop of discord client def on_status(self, status): self.send_message(status._json) def send_message(self, msg): # Submit the coroutine to a given loop future = asyncio.run_coroutine_threadsafe(self.discord(msg), self.loop) # Wait for the result with an optional timeout argument future.result()

不和谐的客户:

class MyClient(discord.Client): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) async def on_ready(self): myStream = tweepy.Stream( auth=api.auth, listener=EpicListener(discord=self.sendtwitter, loop=asyncio.get_event_loop()) ) myStream.filter(follow=['mohitwr'], is_async=True) print(myStream)

希望这会有所帮助。    
© www.soinside.com 2019 - 2024. All rights reserved.