This commit is contained in:
parent
e6d3d13e5d
commit
e700af61cb
12 changed files with 437 additions and 5 deletions
|
@ -27,4 +27,5 @@ Credits:
|
||||||
[Vert Cogs](https://github.com/vertyco/vrt-cogs)
|
[Vert Cogs](https://github.com/vertyco/vrt-cogs)
|
||||||
[Vex Cogs](https://github.com/Vexed01/Vex-Cogs)
|
[Vex Cogs](https://github.com/Vexed01/Vex-Cogs)
|
||||||
[x26 Cogs](https://github.com/Twentysix26/x26-Cogs)
|
[x26 Cogs](https://github.com/Twentysix26/x26-Cogs)
|
||||||
[Toxic Cogs](https://github.com/NeuroAssassin/Toxic-Cogs)
|
[Toxic Cogs](https://github.com/NeuroAssassin/Toxic-Cogs)
|
||||||
|
[Karlo Cogs](https://github.com/karlsbjorn/karlo-cogs)
|
13
autoplay/__init__.py
Normal file
13
autoplay/__init__.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from pylav.extension.red.utils.required_methods import pylav_auto_setup
|
||||||
|
|
||||||
|
from .autoplay import AutoPlay
|
||||||
|
|
||||||
|
with open(Path(__file__).parent / "info.json") as fp:
|
||||||
|
__red_end_user_data_statement__ = json.load(fp)["end_user_data_statement"]
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(bot):
|
||||||
|
await pylav_auto_setup(bot, AutoPlay)
|
240
autoplay/autoplay.py
Normal file
240
autoplay/autoplay.py
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord import AppCommandType
|
||||||
|
from redbot.core import Config, commands
|
||||||
|
from redbot.core.i18n import Translator, cog_i18n
|
||||||
|
|
||||||
|
from pylav.events.player import PlayerDisconnectedEvent
|
||||||
|
from pylav.logging import getLogger
|
||||||
|
from pylav.players.player import Player
|
||||||
|
from pylav.players.query.obj import Query
|
||||||
|
from pylav.type_hints.bot import DISCORD_BOT_TYPE, DISCORD_INTERACTION_TYPE
|
||||||
|
|
||||||
|
log = getLogger("PyLav.3rdpt.karlo-cogs.autoplay")
|
||||||
|
_ = Translator("AutoPlay", __file__)
|
||||||
|
|
||||||
|
|
||||||
|
@cog_i18n(_)
|
||||||
|
class AutoPlay(commands.Cog):
|
||||||
|
"""Automatically play music that a guild member is listening to on Spotify."""
|
||||||
|
|
||||||
|
def __init__(self, bot: DISCORD_BOT_TYPE):
|
||||||
|
self.bot: DISCORD_BOT_TYPE = bot
|
||||||
|
self.config = Config.get_conf(self, identifier=87446677010550784, force_registration=True)
|
||||||
|
default_guild = {"tracked_member": None, "autoplaying": False, "paused_track": None}
|
||||||
|
self.config.register_guild(**default_guild)
|
||||||
|
self.context_user_autoplay = discord.app_commands.ContextMenu(
|
||||||
|
name=_("Start AutoPlay"),
|
||||||
|
callback=self._context_user_autoplay,
|
||||||
|
type=AppCommandType.user,
|
||||||
|
)
|
||||||
|
self.bot.tree.add_command(self.context_user_autoplay)
|
||||||
|
|
||||||
|
@commands.hybrid_command()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def autoplay(self, ctx, member: discord.Member = None):
|
||||||
|
"""Toggle autoplay for a member.
|
||||||
|
|
||||||
|
This will cause the bot to automatically play music that
|
||||||
|
the member is listening to on Spotify.
|
||||||
|
|
||||||
|
To stop it, use `[p]autoplay` without a member, or
|
||||||
|
use a player command like `[p]stop` or `[p]play`.
|
||||||
|
"""
|
||||||
|
if ctx.interaction:
|
||||||
|
await ctx.defer(ephemeral=True)
|
||||||
|
|
||||||
|
if member is None:
|
||||||
|
await self.config.guild(ctx.guild).tracked_member.set(None)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
await self.config.guild(ctx.guild).tracked_member.set(member.id)
|
||||||
|
msg = await self._prepare_autoplay(ctx.guild, ctx.author)
|
||||||
|
await ctx.send(embed=await self.pylav.construct_embed(description=msg, messageable=ctx))
|
||||||
|
|
||||||
|
async def _context_user_autoplay(
|
||||||
|
self, interaction: DISCORD_INTERACTION_TYPE, member: discord.Member
|
||||||
|
):
|
||||||
|
await interaction.response.defer(ephemeral=True)
|
||||||
|
|
||||||
|
if not interaction.guild:
|
||||||
|
await interaction.followup.send(
|
||||||
|
embed=await self.pylav.construct_embed(
|
||||||
|
description=_("This can only be used in a guild."), messageable=interaction
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
await self.config.guild(interaction.guild).tracked_member.set(member.id)
|
||||||
|
|
||||||
|
msg = await self._prepare_autoplay(interaction.guild, interaction.user)
|
||||||
|
await interaction.followup.send(
|
||||||
|
embed=await self.pylav.construct_embed(
|
||||||
|
description=msg.format(member=member.mention),
|
||||||
|
messageable=interaction,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _prepare_autoplay(self, guild, author) -> str:
|
||||||
|
player: Player = self.bot.pylav.get_player(guild.id)
|
||||||
|
|
||||||
|
if not player and author.voice:
|
||||||
|
await self.bot.pylav.connect_player(author, author.voice.channel)
|
||||||
|
player: Player = self.bot.pylav.get_player(guild.id)
|
||||||
|
elif not player:
|
||||||
|
return _(
|
||||||
|
"I am not in a voice channel. Please connect to a voice channel and then use this command."
|
||||||
|
)
|
||||||
|
elif player and player.channel.id != author.voice.channel.id:
|
||||||
|
await player.move_to(author, author.voice.channel)
|
||||||
|
|
||||||
|
if player.is_playing or player.paused:
|
||||||
|
await player.stop(author)
|
||||||
|
if player.queue.size:
|
||||||
|
player.queue.clear()
|
||||||
|
return _(
|
||||||
|
"I'll now play whatever {member} is listening to.\n"
|
||||||
|
"To stop autoplay, use a player command like `stop`"
|
||||||
|
)
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_presence_update(
|
||||||
|
self, member_before: discord.Member, member_after: discord.Member
|
||||||
|
):
|
||||||
|
if await self._member_checks(member_after):
|
||||||
|
return
|
||||||
|
|
||||||
|
player: Player = self.bot.lavalink.get_player(member_after.guild.id)
|
||||||
|
if player is None:
|
||||||
|
log.verbose("No player found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
current_activity = self._get_spotify_activity(member_after)
|
||||||
|
past_activity = self._get_spotify_activity(member_before)
|
||||||
|
if not current_activity:
|
||||||
|
# Member is no longer listening to Spotify.
|
||||||
|
autoplaying = await self.config.guild(member_after.guild).autoplaying()
|
||||||
|
if autoplaying and player.is_playing:
|
||||||
|
await player.set_pause(True, member_after)
|
||||||
|
await self.config.guild(member_after.guild).paused_track.set(
|
||||||
|
past_activity.track_id
|
||||||
|
)
|
||||||
|
return
|
||||||
|
log.verbose(f"Presence update detected. {current_activity.track_url}")
|
||||||
|
if past_activity and past_activity.track_id == current_activity.track_id:
|
||||||
|
# Same track, no need to do anything.
|
||||||
|
return
|
||||||
|
if current_activity.track_id == await self.config.guild(member_after.guild).paused_track():
|
||||||
|
# If the track is the same as when the activity stopped, it was probably paused,
|
||||||
|
# so we'll resume it.
|
||||||
|
log.verbose("Resuming track.")
|
||||||
|
await player.set_pause(False, member_after)
|
||||||
|
return
|
||||||
|
|
||||||
|
log.verbose(f"Querying {current_activity.track_url}")
|
||||||
|
query = await Query.from_string(current_activity.track_url)
|
||||||
|
response = await self.bot.lavalink.search_query(query=query)
|
||||||
|
if response.loadType == "error":
|
||||||
|
log.verbose(f"No tracks found. Response: {response}")
|
||||||
|
return
|
||||||
|
|
||||||
|
log.verbose(f"Query successful: {response.data}")
|
||||||
|
|
||||||
|
if player.paused:
|
||||||
|
# To prevent overlapping tracks, we'll stop the player first to clear the paused track.
|
||||||
|
await player.stop(member_after)
|
||||||
|
if player.queue.size():
|
||||||
|
log.verbose("Queue is not empty, clearing.")
|
||||||
|
player.queue.clear()
|
||||||
|
log.verbose(f"Playing {response.data.info.title}.")
|
||||||
|
await player.play(
|
||||||
|
query=query,
|
||||||
|
track=response.data,
|
||||||
|
requester=member_after,
|
||||||
|
)
|
||||||
|
await self.config.guild(member_after.guild).paused_track.set(None)
|
||||||
|
await self.config.guild(member_after.guild).autoplaying.set(True)
|
||||||
|
|
||||||
|
async def _member_checks(self, member: discord.Member) -> bool:
|
||||||
|
"""Check if the member is valid for autoplay."""
|
||||||
|
return (
|
||||||
|
member.id != tracked_member
|
||||||
|
if (tracked_member := await self.config.guild(member.guild).tracked_member())
|
||||||
|
else True
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_spotify_activity(member: discord.Member) -> Optional[discord.Spotify]:
|
||||||
|
"""Get the Spotify activity of a member."""
|
||||||
|
activity = next(
|
||||||
|
(
|
||||||
|
activity
|
||||||
|
for activity in member.activities
|
||||||
|
if activity.type == discord.ActivityType.listening
|
||||||
|
and hasattr(activity, "track_id")
|
||||||
|
and hasattr(activity, "track_url")
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
return activity if activity and isinstance(activity, discord.Spotify) else None
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_command(self, ctx: commands.Context):
|
||||||
|
"""Stop autoplay when a player command is used."""
|
||||||
|
log.verbose(f"Command {ctx.command.name}, {ctx.command.qualified_name} used.")
|
||||||
|
player_commands = [
|
||||||
|
"play",
|
||||||
|
"skip",
|
||||||
|
"stop",
|
||||||
|
"playlist play",
|
||||||
|
"dc",
|
||||||
|
"prev",
|
||||||
|
"repeat",
|
||||||
|
"shuffle",
|
||||||
|
"pause",
|
||||||
|
"resume",
|
||||||
|
]
|
||||||
|
if ctx.command.name in player_commands and ctx.guild:
|
||||||
|
await self._stop_autoplay(ctx.guild)
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_interaction(self, interaction: discord.Interaction):
|
||||||
|
"""Stop autoplay when a player interaction is used."""
|
||||||
|
if interaction.type != discord.InteractionType.application_command:
|
||||||
|
return
|
||||||
|
log.verbose(
|
||||||
|
f"Interaction {interaction.type}, "
|
||||||
|
f"{interaction.command.name if interaction.command else None} used."
|
||||||
|
)
|
||||||
|
player_commands = [
|
||||||
|
"play",
|
||||||
|
"skip",
|
||||||
|
"stop",
|
||||||
|
"playlist play",
|
||||||
|
"dc",
|
||||||
|
"prev",
|
||||||
|
"repeat",
|
||||||
|
"shuffle",
|
||||||
|
"pause",
|
||||||
|
"resume",
|
||||||
|
]
|
||||||
|
if interaction.command.name in player_commands and interaction.guild:
|
||||||
|
await self._stop_autoplay(interaction.guild)
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_pylav_player_disconnected_event(self, event: PlayerDisconnectedEvent):
|
||||||
|
"""Stop autoplay when the player is disconnected."""
|
||||||
|
guild = event.player.channel.guild
|
||||||
|
log.verbose(f"Player in {guild} disconnected.")
|
||||||
|
if await self.config.guild(guild).autoplaying():
|
||||||
|
await self.pylav.player_state_db_manager.delete_player(guild.id)
|
||||||
|
await self._stop_autoplay(guild)
|
||||||
|
|
||||||
|
async def _stop_autoplay(self, guild: discord.Guild):
|
||||||
|
if not await self.config.guild(guild).autoplaying():
|
||||||
|
return
|
||||||
|
log.verbose("Stopping autoplay.")
|
||||||
|
await self.config.guild(guild).autoplaying.set(False)
|
||||||
|
await self.config.guild(guild).tracked_member.set(None)
|
||||||
|
await self.config.guild(guild).paused_track.set(None)
|
12
autoplay/info.json
Normal file
12
autoplay/info.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"author": ["Karlo"],
|
||||||
|
"description": "Automatically play music that a guild member is listening to on Spotify.\nThis cog REQUIRES a fully set up PyLav and will not work without it.",
|
||||||
|
"short": "Automatically play music that a guild member is listening to on Spotify.",
|
||||||
|
"tags": ["music", "spotify"],
|
||||||
|
"min_python_version": [3, 11, 0],
|
||||||
|
"requirements": ["Py-Lav>=1.10.10"],
|
||||||
|
"min_bot_version": "3.5.0.dev306",
|
||||||
|
"max_bot_version": "3.5.99",
|
||||||
|
"type": "COG",
|
||||||
|
"end_user_data_statement": "This cog does not persistently store data or metadata about users."
|
||||||
|
}
|
42
autoplay/locales/hr-HR.po
Normal file
42
autoplay/locales/hr-HR.po
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: karlo-cogs\n"
|
||||||
|
"POT-Creation-Date: 2025-01-06 18:00+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-02-12 11:32\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Croatian\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: redgettext 3.4.2\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
"X-Crowdin-Project: karlo-cogs\n"
|
||||||
|
"X-Crowdin-Project-ID: 523580\n"
|
||||||
|
"X-Crowdin-Language: hr\n"
|
||||||
|
"X-Crowdin-File: /autoplay/locales/messages.pot\n"
|
||||||
|
"X-Crowdin-File-ID: 37\n"
|
||||||
|
"Language: hr_HR\n"
|
||||||
|
|
||||||
|
#: autoplay/autoplay.py:20
|
||||||
|
#, docstring
|
||||||
|
msgid "Automatically play music that a guild member is listening to on Spotify."
|
||||||
|
msgstr "Automatski reproduciraj glazbu koju neki član guilde sluša na Spotifyu."
|
||||||
|
|
||||||
|
#: autoplay/autoplay.py:28
|
||||||
|
msgid "Start AutoPlay"
|
||||||
|
msgstr "Pokreni AutoPlay"
|
||||||
|
|
||||||
|
#: autoplay/autoplay.py:64
|
||||||
|
msgid "This can only be used in a guild."
|
||||||
|
msgstr "Ovo se može koristiti samo u guildu."
|
||||||
|
|
||||||
|
#: autoplay/autoplay.py:86
|
||||||
|
msgid "I am not in a voice channel. Please connect to a voice channel and then use this command."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: autoplay/autoplay.py:96
|
||||||
|
msgid "I'll now play whatever {member} is listening to.\n"
|
||||||
|
"To stop autoplay, use a player command like `stop`"
|
||||||
|
msgstr "Sada ću pustiti što god {member} sluša.\n"
|
||||||
|
"Za zaustavljanje automatske reprodukcije upotrijebite naredbu playera poput `stop`"
|
||||||
|
|
41
autoplay/locales/sr-SP.po
Normal file
41
autoplay/locales/sr-SP.po
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: karlo-cogs\n"
|
||||||
|
"POT-Creation-Date: 2025-01-06 18:00+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-01-06 18:00\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Serbian (Cyrillic)\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: redgettext 3.4.2\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
"X-Crowdin-Project: karlo-cogs\n"
|
||||||
|
"X-Crowdin-Project-ID: 523580\n"
|
||||||
|
"X-Crowdin-Language: sr\n"
|
||||||
|
"X-Crowdin-File: /autoplay/locales/messages.pot\n"
|
||||||
|
"X-Crowdin-File-ID: 37\n"
|
||||||
|
"Language: sr_SP\n"
|
||||||
|
|
||||||
|
#: autoplay/autoplay.py:20
|
||||||
|
#, docstring
|
||||||
|
msgid "Automatically play music that a guild member is listening to on Spotify."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: autoplay/autoplay.py:28
|
||||||
|
msgid "Start AutoPlay"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: autoplay/autoplay.py:64
|
||||||
|
msgid "This can only be used in a guild."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: autoplay/autoplay.py:86
|
||||||
|
msgid "I am not in a voice channel. Please connect to a voice channel and then use this command."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: autoplay/autoplay.py:96
|
||||||
|
msgid "I'll now play whatever {member} is listening to.\n"
|
||||||
|
"To stop autoplay, use a player command like `stop`"
|
||||||
|
msgstr ""
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"author": [
|
"author": [
|
||||||
"Valerie (1050531216589332581)"
|
"Valerie",
|
||||||
|
"Rose Haven Network"
|
||||||
],
|
],
|
||||||
"install_msg": "Thanks for adding Ruby Cogs! Join our Discord @ https://discord.gg/5CA8sewarU",
|
"install_msg": "Thanks for adding Ruby Cogs! Join our Discord @ https://discord.gg/5CA8sewarU",
|
||||||
"name": "Ruby Cogs",
|
"name": "Ruby Cogs",
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
"Valerie"
|
"Valerie"
|
||||||
],
|
],
|
||||||
"install_msg": "Thank you for installing Modrinth Tracker",
|
"install_msg": "Thank you for installing Modrinth Tracker",
|
||||||
"name": "Modrinth tracker",
|
"name": "Modrinth Tracker",
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"short": "Track Modrinth Projects",
|
"short": "Modrinth Projects Tracker",
|
||||||
"description": "Track Modrinth Projects..",
|
"description": "A cog for tracking Modrinth Projects via their project ID, using Modrinth's API",
|
||||||
"tags": [
|
"tags": [
|
||||||
"modrinth"
|
"modrinth"
|
||||||
],
|
],
|
||||||
|
|
33
silentreplier/__init__.py
Normal file
33
silentreplier/__init__.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
"""
|
||||||
|
A large majority of the code here is from Zephyrkul's cmdreplier
|
||||||
|
https://github.com/Zephyrkul/FluffyCogs/blob/master/cmdreplier/__init__.py
|
||||||
|
Thanks Zeph
|
||||||
|
"""
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from redbot.core import commands
|
||||||
|
from redbot.core.bot import Red
|
||||||
|
|
||||||
|
|
||||||
|
async def silent_send(__sender, /, *args, **kwargs):
|
||||||
|
ctx: commands.Context = __sender.__self__
|
||||||
|
if not ctx.command_failed and "silent" not in kwargs:
|
||||||
|
kwargs["silent"] = True
|
||||||
|
return await __sender(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
async def before_hook(ctx: commands.Context):
|
||||||
|
if not ctx.message.edited_at and ctx.message.flags.silent:
|
||||||
|
with contextlib.suppress(AttributeError):
|
||||||
|
del ctx.send
|
||||||
|
ctx.send = partial(silent_send, ctx.send)
|
||||||
|
|
||||||
|
|
||||||
|
async def setup(bot: Red):
|
||||||
|
bot.before_invoke(before_hook)
|
||||||
|
|
||||||
|
|
||||||
|
async def teardown(bot: Red):
|
||||||
|
bot.remove_before_invoke_hook(before_hook)
|
11
silentreplier/info.json
Normal file
11
silentreplier/info.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"author": ["Karlo"],
|
||||||
|
"description": "Have the bot silently respond to commands which have been prefixed with @silent",
|
||||||
|
"short": "Have the bot silently respond to commands which have been prefixed with @silent",
|
||||||
|
"install_msg": "This cog has no commands. It will silently respond to commands which have been prefixed with @silent",
|
||||||
|
"tags": ["utility"],
|
||||||
|
"min_bot_version": "3.5.0.dev329",
|
||||||
|
"max_bot_version": "3.6.0.dev0",
|
||||||
|
"type": "COG",
|
||||||
|
"end_user_data_statement": "This cog does not persistently store any data or metadata about users."
|
||||||
|
}
|
19
silentreplier/locales/hr-HR.po
Normal file
19
silentreplier/locales/hr-HR.po
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: karlo-cogs\n"
|
||||||
|
"POT-Creation-Date: 2023-03-29 12:09+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-02-12 11:32\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Croatian\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: redgettext 3.4.2\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
"X-Crowdin-Project: karlo-cogs\n"
|
||||||
|
"X-Crowdin-Project-ID: 523580\n"
|
||||||
|
"X-Crowdin-Language: hr\n"
|
||||||
|
"X-Crowdin-File: /silentreplier/locales/messages.pot\n"
|
||||||
|
"X-Crowdin-File-ID: 43\n"
|
||||||
|
"Language: hr_HR\n"
|
||||||
|
|
19
silentreplier/locales/sr-SP.po
Normal file
19
silentreplier/locales/sr-SP.po
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: karlo-cogs\n"
|
||||||
|
"POT-Creation-Date: 2023-03-29 12:09+0000\n"
|
||||||
|
"PO-Revision-Date: 2023-03-29 12:10\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Serbian (Cyrillic)\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: redgettext 3.4.2\n"
|
||||||
|
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||||
|
"X-Crowdin-Project: karlo-cogs\n"
|
||||||
|
"X-Crowdin-Project-ID: 523580\n"
|
||||||
|
"X-Crowdin-Language: sr\n"
|
||||||
|
"X-Crowdin-File: /silentreplier/locales/messages.pot\n"
|
||||||
|
"X-Crowdin-File-ID: 43\n"
|
||||||
|
"Language: sr_SP\n"
|
||||||
|
|
Loading…
Add table
Reference in a new issue