mirror of
https://github.com/YGGverse/HLState.git
synced 2025-02-05 19:54:13 +00:00
implement online history
This commit is contained in:
parent
2071796a07
commit
272d219066
39
migrations/Version20240106175257.php
Normal file
39
migrations/Version20240106175257.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-generated Migration: Please modify to your needs!
|
||||||
|
*/
|
||||||
|
final class Version20240106175257 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this up() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('CREATE TABLE online (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, crc32server BIGINT NOT NULL, time BIGINT NOT NULL, total INTEGER NOT NULL, players INTEGER NOT NULL, bots INTEGER NOT NULL)');
|
||||||
|
$this->addSql('CREATE TABLE messenger_messages (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, body CLOB NOT NULL, headers CLOB NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL --(DC2Type:datetime_immutable)
|
||||||
|
, available_at DATETIME NOT NULL --(DC2Type:datetime_immutable)
|
||||||
|
, delivered_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable)
|
||||||
|
)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_75EA56E0FB7336F0 ON messenger_messages (queue_name)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_75EA56E0E3BD61CE ON messenger_messages (available_at)');
|
||||||
|
$this->addSql('CREATE INDEX IDX_75EA56E016BA31DB ON messenger_messages (delivered_at)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
// this down() migration is auto-generated, please modify it to your needs
|
||||||
|
$this->addSql('DROP TABLE online');
|
||||||
|
$this->addSql('DROP TABLE messenger_messages');
|
||||||
|
}
|
||||||
|
}
|
137
src/Controller/CrontabController.php
Normal file
137
src/Controller/CrontabController.php
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
use App\Entity\Online;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
class CrontabController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route(
|
||||||
|
'/crontab/online',
|
||||||
|
name: 'crontab_online',
|
||||||
|
methods:
|
||||||
|
[
|
||||||
|
'GET'
|
||||||
|
]
|
||||||
|
)]
|
||||||
|
public function online(
|
||||||
|
?Request $request,
|
||||||
|
EntityManagerInterface $entityManagerInterface
|
||||||
|
): Response
|
||||||
|
{
|
||||||
|
// Get HLServers config
|
||||||
|
if ($hlservers = file_get_contents($this->getParameter('app.hlservers')))
|
||||||
|
{
|
||||||
|
$hlservers = json_decode($hlservers);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$hlservers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect servers info
|
||||||
|
$servers = [];
|
||||||
|
|
||||||
|
foreach ($hlservers as $hlserver)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$server = new \xPaw\SourceQuery\SourceQuery();
|
||||||
|
|
||||||
|
$server->Connect(
|
||||||
|
$hlserver->host,
|
||||||
|
$hlserver->port
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($server->Ping())
|
||||||
|
{
|
||||||
|
if ($info = (array) $server->GetInfo())
|
||||||
|
{
|
||||||
|
// Filter response
|
||||||
|
$bots = isset($info['Bots']) && $info['Bots'] > 0 ? (int) $info['Bots'] : 0;
|
||||||
|
$players = isset($info['Players']) && $info['Players'] > 0 ? (int) $info['Players'] - $bots : 0;
|
||||||
|
$total = $players + $bots;
|
||||||
|
|
||||||
|
// Generate CRC32 server ID
|
||||||
|
$crc32Server = crc32(
|
||||||
|
$hlserver->host . ':' . $hlserver->port
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get last online value
|
||||||
|
$online = $entityManagerInterface->getRepository(Online::class)->findOneBy(
|
||||||
|
[
|
||||||
|
'crc32server' => $crc32Server
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'DESC' // same as online.time but faster
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add new record if online changed
|
||||||
|
if
|
||||||
|
(
|
||||||
|
is_null($online)
|
||||||
|
||
|
||||||
|
$players !== $online->getPlayers()
|
||||||
|
||
|
||||||
|
$bots !== $online->getBots()
|
||||||
|
||
|
||||||
|
$total !== $online->getTotal()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$online = new Online();
|
||||||
|
|
||||||
|
$online->setCrc32server(
|
||||||
|
$crc32Server
|
||||||
|
);
|
||||||
|
|
||||||
|
$online->setTime(
|
||||||
|
time()
|
||||||
|
);
|
||||||
|
|
||||||
|
$online->setPlayers(
|
||||||
|
$players
|
||||||
|
);
|
||||||
|
|
||||||
|
$online->setBots(
|
||||||
|
$bots
|
||||||
|
);
|
||||||
|
|
||||||
|
$online->setTotal(
|
||||||
|
$total
|
||||||
|
);
|
||||||
|
|
||||||
|
$entityManagerInterface->persist(
|
||||||
|
$online
|
||||||
|
);
|
||||||
|
|
||||||
|
$entityManagerInterface->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (Exception $error)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
$server->Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render response
|
||||||
|
return new Response(); // @TODO
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,9 @@ use Symfony\Component\Routing\Annotation\Route;
|
|||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
use App\Entity\Online;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
class MainController extends AbstractController
|
class MainController extends AbstractController
|
||||||
{
|
{
|
||||||
#[Route(
|
#[Route(
|
||||||
@ -20,7 +23,8 @@ class MainController extends AbstractController
|
|||||||
]
|
]
|
||||||
)]
|
)]
|
||||||
public function index(
|
public function index(
|
||||||
?Request $request
|
?Request $request,
|
||||||
|
EntityManagerInterface $entityManagerInterface
|
||||||
): Response
|
): Response
|
||||||
{
|
{
|
||||||
// Get HLServers config
|
// Get HLServers config
|
||||||
@ -52,15 +56,49 @@ class MainController extends AbstractController
|
|||||||
{
|
{
|
||||||
if ($info = (array) $server->GetInfo())
|
if ($info = (array) $server->GetInfo())
|
||||||
{
|
{
|
||||||
|
// Generate CRC32 ID
|
||||||
|
$crc32server = crc32(
|
||||||
|
$hlserver->host . ':' . $hlserver->port
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get session
|
||||||
|
$session = empty($info['Players']) ? [] : (array) $server->GetPlayers();
|
||||||
|
|
||||||
|
// Sort by players by frags
|
||||||
|
if ($session)
|
||||||
|
{
|
||||||
|
array_multisort(
|
||||||
|
array_column(
|
||||||
|
$session,
|
||||||
|
'Frags'
|
||||||
|
),
|
||||||
|
SORT_DESC,
|
||||||
|
$session
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get online
|
||||||
|
$online = $entityManagerInterface->getRepository(Online::class)->findBy(
|
||||||
|
[
|
||||||
|
'crc32server' => $crc32server
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'DESC' // same as online.time but faster
|
||||||
|
],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add server
|
||||||
$servers[] = [
|
$servers[] = [
|
||||||
'host' => $hlserver->host,
|
'crc32server' => $crc32server,
|
||||||
'port' => $hlserver->port,
|
'host' => $hlserver->host,
|
||||||
'alias' => $hlserver->alias,
|
'port' => $hlserver->port,
|
||||||
'info' => $info,
|
'alias' => $hlserver->alias,
|
||||||
'online' => empty($info['Players']) ? [] : (array) $server->GetPlayers()
|
'info' => $info,
|
||||||
|
'session' => $session,
|
||||||
|
'online' => $online
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
109
src/Controller/RssController.php
Normal file
109
src/Controller/RssController.php
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
|
use App\Entity\Online;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
|
||||||
|
class RssController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route(
|
||||||
|
'/rss/online/{crc32server}',
|
||||||
|
name: 'rss_online',
|
||||||
|
requirements:
|
||||||
|
[
|
||||||
|
'crc32server' => '\d+',
|
||||||
|
],
|
||||||
|
methods:
|
||||||
|
[
|
||||||
|
'GET'
|
||||||
|
]
|
||||||
|
)]
|
||||||
|
public function online(
|
||||||
|
?Request $request,
|
||||||
|
EntityManagerInterface $entityManagerInterface
|
||||||
|
): Response
|
||||||
|
{
|
||||||
|
// Get HLServers config
|
||||||
|
if ($hlservers = file_get_contents($this->getParameter('app.hlservers')))
|
||||||
|
{
|
||||||
|
$hlservers = json_decode($hlservers);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$hlservers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find server info
|
||||||
|
foreach ($hlservers as $hlserver)
|
||||||
|
{
|
||||||
|
// Generate CRC32 server ID
|
||||||
|
$crc32server = crc32(
|
||||||
|
$hlserver->host . ':' . $hlserver->port
|
||||||
|
);
|
||||||
|
|
||||||
|
// Skip servers not registered in HLServers
|
||||||
|
if ($crc32server != $request->get('crc32server'))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get last online value
|
||||||
|
$online = $entityManagerInterface->getRepository(Online::class)->findBy(
|
||||||
|
[
|
||||||
|
'crc32server' => $crc32server
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 'DESC' // same as online.time but faster
|
||||||
|
],
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($online as $value)
|
||||||
|
{
|
||||||
|
$result[] =
|
||||||
|
[
|
||||||
|
'id' => $value->getId(),
|
||||||
|
'bots' => $value->getBots(),
|
||||||
|
'players' => $value->getPlayers(),
|
||||||
|
'total' => $value->getTotal(),
|
||||||
|
'time' => $value->getTime()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response
|
||||||
|
$response = new Response();
|
||||||
|
|
||||||
|
$response->headers->set(
|
||||||
|
'Content-Type',
|
||||||
|
'text/xml'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->render(
|
||||||
|
'default/rss/online.xml.twig',
|
||||||
|
[
|
||||||
|
'server' =>
|
||||||
|
[
|
||||||
|
'crc32' => $crc32server,
|
||||||
|
'host' => $hlserver->host,
|
||||||
|
'port' => $hlserver->port,
|
||||||
|
],
|
||||||
|
'online' => $result
|
||||||
|
],
|
||||||
|
$response
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $this->createNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
96
src/Entity/Online.php
Normal file
96
src/Entity/Online.php
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Repository\OnlineRepository;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
#[ORM\Entity(repositoryClass: OnlineRepository::class)]
|
||||||
|
class Online
|
||||||
|
{
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\GeneratedValue]
|
||||||
|
#[ORM\Column]
|
||||||
|
private ?int $id = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::BIGINT)]
|
||||||
|
private ?int $crc32server = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::BIGINT)]
|
||||||
|
private ?int $time = null;
|
||||||
|
|
||||||
|
#[ORM\Column]
|
||||||
|
private ?int $total = null;
|
||||||
|
|
||||||
|
#[ORM\Column]
|
||||||
|
private ?int $players = null;
|
||||||
|
|
||||||
|
#[ORM\Column]
|
||||||
|
private ?int $bots = null;
|
||||||
|
|
||||||
|
public function getId(): ?int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCrc32server(): ?int
|
||||||
|
{
|
||||||
|
return $this->crc32server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCrc32server(int $crc32server): static
|
||||||
|
{
|
||||||
|
$this->crc32server = $crc32server;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTime(): ?int
|
||||||
|
{
|
||||||
|
return $this->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTime(int $time): static
|
||||||
|
{
|
||||||
|
$this->time = $time;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTotal(): ?int
|
||||||
|
{
|
||||||
|
return $this->total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTotal(int $total): static
|
||||||
|
{
|
||||||
|
$this->total = $total;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlayers(): ?int
|
||||||
|
{
|
||||||
|
return $this->players;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlayers(int $players): static
|
||||||
|
{
|
||||||
|
$this->players = $players;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBots(): ?int
|
||||||
|
{
|
||||||
|
return $this->bots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBots(int $bots): static
|
||||||
|
{
|
||||||
|
$this->bots = $bots;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
23
src/Repository/OnlineRepository.php
Normal file
23
src/Repository/OnlineRepository.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Entity\Online;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends ServiceEntityRepository<Online>
|
||||||
|
*
|
||||||
|
* @method Online|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
|
* @method Online|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
|
* @method Online[] findAll()
|
||||||
|
* @method Online[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
|
*/
|
||||||
|
class OnlineRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, Online::class);
|
||||||
|
}
|
||||||
|
}
|
@ -27,15 +27,15 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if server.online %}
|
{% if server.session %}
|
||||||
<h3 class="padding-y-8-px text-align-right">{{ 'Online' | trans }}</h3>
|
<h3 class="padding-y-8-px text-align-right">{{ 'Session' | trans }}</h3>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-align-left">{{ 'Name' | trans }}</th>
|
<th class="text-align-left">{{ 'Name' | trans }}</th>
|
||||||
<th class="text-align-center">{{ 'Frags' | trans }}</th>
|
<th class="text-align-center">{{ 'Frags' | trans }}</th>
|
||||||
<th class="text-align-center">{{ 'Time' | trans }}</th>
|
<th class="text-align-center">{{ 'Time' | trans }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for player in server.online %}
|
{% for player in server.session %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-align-left">{{ player.Name }}</td>
|
<td class="text-align-left">{{ player.Name }}</td>
|
||||||
<td class="text-align-center">{{ player.Frags }}</td>
|
<td class="text-align-center">{{ player.Frags }}</td>
|
||||||
@ -52,6 +52,30 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if server.online %}
|
||||||
|
<h3 class="padding-y-8-px text-align-right">
|
||||||
|
{{ 'Online' | trans }}
|
||||||
|
<a href="{{ path('rss_online', { crc32server : server.crc32server }) }}" title="{{ 'RSS' | trans }}"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
|
||||||
|
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0 8 8 0 0 0-8-8 1 1 0 0 1 0-2m0 4a6 6 0 0 1 6 6 1 1 0 1 1-2 0 4 4 0 0 0-4-4 1 1 0 0 1 0-2m.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3"/>
|
||||||
|
</svg></a>
|
||||||
|
</h3>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th class="text-align-left">{{ 'Time' | trans }}</th>
|
||||||
|
<th class="text-align-center">{{ 'Players' | trans }}</th>
|
||||||
|
<th class="text-align-center">{{ 'Bots' | trans }}</th>
|
||||||
|
<th class="text-align-center">{{ 'Total' | trans }}</th>
|
||||||
|
</tr>
|
||||||
|
{% for online in server.online %}
|
||||||
|
<tr>
|
||||||
|
<td class="text-align-left">{{ online.time | format_datetime }}</td>
|
||||||
|
<td class="text-align-center">{{ online.players }}</td>
|
||||||
|
<td class="text-align-center">{{ online.bots }}</td>
|
||||||
|
<td class="text-align-center">{{ online.total }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endblock %}
|
{% endblock %}
|
19
templates/default/rss/online.xml.twig
Normal file
19
templates/default/rss/online.xml.twig
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<channel>
|
||||||
|
<atom:link href="{{ url('main_index') }}" rel="self" type="application/rss+xml"></atom:link>
|
||||||
|
<title>{{ server.host }}:{{ server.port }} - {{ app.name }}</title>
|
||||||
|
<link>{{ url('main_index') }}</link>
|
||||||
|
{% for value in online %}
|
||||||
|
<item>
|
||||||
|
<title>{{ 'Online changed!' | trans }}</title>
|
||||||
|
<guid>{{ value.id }}</guid>
|
||||||
|
<description>
|
||||||
|
{{ 'Players' | trans }}: {{ value.players }}
|
||||||
|
{{ 'Bots' | trans }}: {{ value.bots }}
|
||||||
|
{{ 'Total' | trans }}: {{ value.total }}
|
||||||
|
</description>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
</channel>
|
||||||
|
</rss>
|
Loading…
x
Reference in New Issue
Block a user