SeaCogs/bible/bible.py

319 lines
12 KiB
Python

# _____ _
# / ____| (_)
# | (___ ___ __ _ _____ ___ _ __ ___ _ __ ___ ___ _ __
# \___ \ / _ \/ _` / __\ \ /\ / / | '_ ` _ \| '_ ` _ \ / _ \ '__|
# ____) | __/ (_| \__ \\ V V /| | | | | | | | | | | | __/ |
# |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_|
import json
import logging
import random
import aiohttp
from discord import Embed
from redbot.core import Config, commands
from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import error
import bible.errors
class Bible(commands.Cog):
"""Retrieve Bible verses from the API.bible API."""
__author__ = "SeaswimmerTheFsh"
__version__ = "1.0.0"
def __init__(self, bot: Red):
super().__init__()
self.bot = bot
self.session = aiohttp.ClientSession()
self.config = Config.get_conf(
self, identifier=481923957134912, force_registration=True
)
self.logger = logging.getLogger("red.sea.bible")
self.config.register_global(bible="de4e12af7f28f599-02")
self.config.register_user(bible=None)
async def translate_book_name(
self, ctx: commands.Context, bible_id: str, book_name: str
) -> str:
"""Translate a book name to a book ID."""
book_name_list = [
w.lower() if w.lower() == "of" else w.title() for w in book_name.split()
]
book_name = " ".join(book_name_list)
books = await self._get_books(ctx, bible_id)
for book in books:
if book_name in (book["abbreviation"], book["name"]):
return book["id"]
raise ValueError(error(f"Book {book_name} not found."))
async def _get_passage(
self,
ctx: commands.Context,
bible_id: str,
passage_id: str,
include_verse_numbers: bool,
) -> dict:
"""Get a Bible passage from the API.bible API."""
url = f"https://api.scripture.api.bible/v1/bibles/{bible_id}/passages/{passage_id}"
headers = await self.bot.get_shared_api_tokens("api.bible")
params = {
"fums-version": "3",
"content-type": "text",
"include-notes": "false",
"include-titles": "false",
"include-chapter-numbers": "false",
"include-verse-numbers": str(include_verse_numbers).lower(),
"include-verse-spans": "false",
"use-org-id": "false",
}
async with self.session.get(url, headers=headers, params=params) as response:
data = await response.json()
self.logger.debug(
"_get_passage executed with a response code of: %s\n%s",
response.status,
json.dumps(data),
)
if response.status == 401:
raise bible.errors.Unauthorized()
if response.status == 403:
raise bible.errors.BibleAccessError()
if response.status == 404:
raise bible.errors.NotFound()
if response.status == 503:
raise bible.errors.ServiceUnavailable()
fums_url = "https://fums.api.bible/f3"
fums_params = {
"t": data["meta"]["fumsToken"],
"dId": "discord-" + str(self.bot.user.id),
"sId": "discord-" + str(ctx.message.created_at.timestamp()),
"uId": ctx.author.id,
}
async with self.session.get(fums_url, params=fums_params) as response:
fums_data = await response.json()
self.logger.debug(
"_get_passage FUMS executed with a response code of: %s\n%s",
response.status,
json.dumps(fums_data),
)
return data["data"]
async def _get_books(self, ctx: commands.Context, bible_id: str) -> dict:
"""Get the books of the Bible from the API.bible API."""
url = f"https://api.scripture.api.bible/v1/bibles/{bible_id}/books"
headers = await self.bot.get_shared_api_tokens("api.bible")
params = {
"fums-version": "3",
}
async with self.session.get(url, headers=headers, params=params) as response:
data = await response.json()
self.logger.debug(
"_get_books executed with a response code of: %s\nURL: %s\n%s",
response.status,
response.url,
json.dumps(data),
)
if response.status == 401:
raise bible.errors.Unauthorized()
if response.status == 403:
raise bible.errors.BibleAccessError()
if response.status == 503:
raise bible.errors.ServiceUnavailable()
fums_url = "https://fums.api.bible/f3"
fums_params = {
"t": data["meta"]["fumsToken"],
"dId": "discord-" + str(self.bot.user.id),
"sId": "discord-" + str(ctx.message.created_at.timestamp()),
"uId": ctx.author.id,
}
async with self.session.get(fums_url, params=fums_params) as response:
fums_data = await response.json()
self.logger.debug(
"_get_books FUMS executed with a response code of: %s\n%s",
response.status,
json.dumps(fums_data),
)
return data["data"]
async def _get_chapters(
self, ctx: commands.Context, bible_id: str, book_id: str
) -> dict:
"""Get the chapters of a book from the API.bible API."""
url = f"https://api.scripture.api.bible/v1/bibles/{bible_id}/books/{book_id}/chapters"
headers = await self.bot.get_shared_api_tokens("api.bible")
params = {
"fums-version": "3",
}
async with self.session.get(url, headers=headers, params=params) as response:
data = await response.json()
self.logger.debug(
"_get_chapters executed with a response code of: %s\n%s",
response.status,
json.dumps(data),
)
if response.status == 401:
raise bible.errors.Unauthorized()
if response.status == 403:
raise bible.errors.BibleAccessError()
if response.status == 503:
raise bible.errors.ServiceUnavailable()
fums_url = "https://fums.api.bible/f3"
fums_params = {
"t": data["meta"]["fumsToken"],
"dId": "discord-" + str(self.bot.user.id),
"sId": "discord-" + str(ctx.message.created_at.timestamp()),
"uId": ctx.author.id,
}
async with self.session.get(fums_url, params=fums_params) as response:
fums_data = await response.json()
self.logger.debug(
"_get_chapters FUMS executed with a response code of: %s\n%s",
response.status,
json.dumps(fums_data),
)
return data["data"]
async def _get_verses(
self, ctx: commands.Context, bible_id: str, book_id: str, chapter: int
) -> dict:
"""Get the verses of a chapter from the API.bible API."""
url = f"https://api.scripture.api.bible/v1/bibles/{bible_id}/chapters/{book_id}.{chapter}/verses"
headers = await self.bot.get_shared_api_tokens("api.bible")
params = {
"fums-version": "3",
}
async with self.session.get(url, headers=headers, params=params) as response:
data = await response.json()
self.logger.debug(
"_get_verses executed with a response code of: %s\n%s",
response.status,
json.dumps(data),
)
if response.status == 401:
raise bible.errors.Unauthorized()
if response.status == 403:
raise bible.errors.BibleAccessError()
if response.status == 503:
raise bible.errors.ServiceUnavailable()
fums_url = "https://fums.api.bible/f3"
fums_params = {
"t": data["meta"]["fumsToken"],
"dId": "discord-" + str(self.bot.user.id),
"sId": "discord-" + str(ctx.message.created_at.timestamp()),
"uId": ctx.author.id,
}
async with self.session.get(fums_url, params=fums_params) as response:
fums_data = await response.json()
self.logger.debug(
"_get_verses FUMS executed with a response code of: %s\n%s",
response.status,
json.dumps(fums_data),
)
return data["data"]
@commands.group(autohelp=True)
async def bible(self, ctx: commands.Context):
"""Retrieve Bible verses from the API.bible API."""
@bible.command(name="passage", aliases=["verse"])
async def bible_passage(self, ctx: commands.Context, book: str, passage: str):
"""Get a Bible passage.
Example usage:
`[p]bible passage Genesis 1:1`
`[p]bible passage John 3:16-3:17`"""
bible_id = await self.config.bible()
try:
book_id = await self.translate_book_name(ctx, bible_id, book)
except ValueError as e:
await ctx.send(str(e))
return
try:
if len(passage.split("-")) == 2:
from_verse, to_verse = passage.replace(":", ".").split("-")
if "." not in to_verse:
to_verse = f"{from_verse.split('.')[0]}.{to_verse}"
passage = await self._get_passage(
ctx, bible_id, f"{book_id}.{from_verse}-{book_id}.{to_verse}", True
)
else:
passage = await self._get_passage(
ctx, bible_id, f"{book_id}.{passage.replace(':', '.')}", False
)
except (
bible.errors.BibleAccessError,
bible.errors.NotFound,
bible.errors.ServiceUnavailable,
bible.errors.Unauthorized,
) as e:
await ctx.send(e.message)
return
if len(passage["content"]) > 4096:
await ctx.send("The passage is too long to send.")
return
if await ctx.embed_requested():
embed = Embed(
title=f"{passage['reference']}",
description=passage["content"].replace("", ""),
color=await self.bot.get_embed_color(ctx.channel),
)
embed.set_footer(text=f"{ctx.prefix}bible passage - Powered by API.Bible")
await ctx.send(embed=embed)
else:
await ctx.send(f"## {passage['reference']}\n{passage['content']}")
@bible.command(name="random")
async def bible_random(self, ctx: commands.Context):
"""Get a random Bible verse."""
bible_id = await self.config.bible()
try:
books = await self._get_books(ctx, bible_id)
book = random.choice(books)
chapters = await self._get_chapters(ctx, bible_id, book["id"])
chapter = random.choice(chapters)
verses = await self._get_verses(
ctx, bible_id, book["id"], chapter["number"]
)
verse = random.choice(verses)["id"]
passage = await self._get_passage(ctx, bible_id, verse, False)
except (
bible.errors.BibleAccessError,
bible.errors.NotFound,
bible.errors.ServiceUnavailable,
bible.errors.Unauthorized,
) as e:
await ctx.send(e.message)
return
if await ctx.embed_requested():
embed = Embed(
title=f"{passage['reference']}",
description=passage["content"].replace("", ""),
color=await self.bot.get_embed_color(ctx.channel),
)
embed.set_footer(text=f"{ctx.prefix}bible random - Powered by API.Bible")
await ctx.send(embed=embed)
else:
await ctx.send(f"## {passage['reference']}\n{passage['content']}")