mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2024-12-23 21:00:24 +03:00
476 lines
15 KiB
PHP
476 lines
15 KiB
PHP
<?php
|
|
|
|
class CodebergBridge extends BridgeAbstract
|
|
{
|
|
const NAME = 'Codeberg Bridge';
|
|
const URI = 'https://codeberg.org/';
|
|
const DESCRIPTION = 'Returns commits, issues, pull requests or releases for a repository.';
|
|
const MAINTAINER = 'VerifiedJoseph';
|
|
const PARAMETERS = [
|
|
'Commits' => [
|
|
'branch' => [
|
|
'name' => 'branch',
|
|
'type' => 'text',
|
|
'exampleValue' => 'main',
|
|
'required' => false,
|
|
'title' => 'Optional, main branch is used by default.',
|
|
],
|
|
],
|
|
'Issues' => [],
|
|
'Issue Comments' => [
|
|
'issueId' => [
|
|
'name' => 'Issue ID',
|
|
'type' => 'text',
|
|
'required' => true,
|
|
'exampleValue' => '513',
|
|
]
|
|
],
|
|
'Pull Requests' => [],
|
|
'Releases' => [],
|
|
'Tags' => [],
|
|
'global' => [
|
|
'username' => [
|
|
'name' => 'Username',
|
|
'type' => 'text',
|
|
'exampleValue' => 'Codeberg',
|
|
'title' => 'Username of account that the repository belongs to.',
|
|
'required' => true,
|
|
],
|
|
'repo' => [
|
|
'name' => 'Repository',
|
|
'type' => 'text',
|
|
'exampleValue' => 'Community',
|
|
'required' => true,
|
|
]
|
|
]
|
|
];
|
|
|
|
const CACHE_TIMEOUT = 1800;
|
|
|
|
const TEST_DETECT_PARAMETERS = [
|
|
'https://codeberg.org/Codeberg/Community/issues/507' => [
|
|
'context' => 'Issue Comments', 'username' => 'Codeberg', 'repo' => 'Community', 'issueId' => '507'
|
|
],
|
|
'https://codeberg.org/Codeberg/Community/issues' => [
|
|
'context' => 'Issues', 'username' => 'Codeberg', 'repo' => 'Community'
|
|
],
|
|
'https://codeberg.org/Codeberg/Community/pulls' => [
|
|
'context' => 'Pull Requests', 'username' => 'Codeberg', 'repo' => 'Community'
|
|
],
|
|
'https://codeberg.org/Codeberg/Community/releases' => [
|
|
'context' => 'Releases', 'username' => 'Codeberg', 'repo' => 'Community'
|
|
],
|
|
'https://codeberg.org/Codeberg/Community/commits/branch/master' => [
|
|
'context' => 'Commits', 'username' => 'Codeberg', 'repo' => 'Community', 'branch' => 'master'
|
|
],
|
|
'https://codeberg.org/Codeberg/Community/commits' => [
|
|
'context' => 'Commits', 'username' => 'Codeberg', 'repo' => 'Community'
|
|
]
|
|
];
|
|
|
|
private $defaultBranch = 'main';
|
|
private $issueTitle = '';
|
|
|
|
private $urlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)(?:\/commits\/branch\/([\w]+))?/';
|
|
private $issuesUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/issues/';
|
|
private $pullsUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/pulls/';
|
|
private $releasesUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/releases/';
|
|
private $issueCommentsUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/issues\/([0-9]+)/';
|
|
|
|
public function collectData()
|
|
{
|
|
$url = $this->getURI();
|
|
$html = getSimpleHTMLDOM($url);
|
|
$html = defaultLinkTo($html, $url);
|
|
|
|
switch ($this->queriedContext) {
|
|
case 'Commits':
|
|
$this->extractCommits($html);
|
|
break;
|
|
case 'Issues':
|
|
$this->extractIssues($html);
|
|
break;
|
|
case 'Issue Comments':
|
|
$this->extractIssueComments($html);
|
|
break;
|
|
case 'Pull Requests':
|
|
$this->extractPulls($html);
|
|
break;
|
|
case 'Releases':
|
|
$this->extractReleases($html);
|
|
break;
|
|
case 'Tags':
|
|
$this->extractTags($html);
|
|
break;
|
|
default:
|
|
throw new \Exception('Invalid context: ' . $this->queriedContext);
|
|
}
|
|
}
|
|
|
|
public function getName()
|
|
{
|
|
switch ($this->queriedContext) {
|
|
case 'Commits':
|
|
if ($this->getBranch() === $this->defaultBranch) {
|
|
return $this->getRepo() . ' Commits';
|
|
}
|
|
|
|
return $this->getRepo() . ' Commits (' . $this->getBranch() . ' branch) - ' . self::NAME;
|
|
case 'Issues':
|
|
return $this->getRepo() . ' Issues - ' . self::NAME;
|
|
case 'Issue Comments':
|
|
return $this->issueTitle . ' - Issue Comments - ' . self::NAME;
|
|
case 'Pull Requests':
|
|
return $this->getRepo() . ' Pull Requests - ' . self::NAME;
|
|
case 'Releases':
|
|
return $this->getRepo() . ' Releases - ' . self::NAME;
|
|
case 'Tags':
|
|
return $this->getRepo() . ' Tags - ' . self::NAME;
|
|
default:
|
|
return parent::getName();
|
|
}
|
|
}
|
|
|
|
public function getURI()
|
|
{
|
|
switch ($this->queriedContext) {
|
|
case 'Commits':
|
|
return self::URI . $this->getRepo() . '/commits/branch/' . $this->getBranch();
|
|
case 'Issues':
|
|
return self::URI . $this->getRepo() . '/issues/';
|
|
case 'Issue Comments':
|
|
return self::URI . $this->getRepo() . '/issues/' . $this->getInput('issueId');
|
|
case 'Pull Requests':
|
|
return self::URI . $this->getRepo() . '/pulls';
|
|
case 'Releases':
|
|
return self::URI . $this->getRepo() . '/releases';
|
|
case 'Tags':
|
|
return self::URI . $this->getRepo() . '/tags';
|
|
default:
|
|
return parent::getURI();
|
|
}
|
|
}
|
|
|
|
private function getBranch()
|
|
{
|
|
if ($this->getInput('branch')) {
|
|
return $this->getInput('branch');
|
|
}
|
|
|
|
return $this->defaultBranch;
|
|
}
|
|
|
|
private function getRepo()
|
|
{
|
|
return $this->getInput('username') . '/' . $this->getInput('repo');
|
|
}
|
|
|
|
/**
|
|
* Extract commits
|
|
*/
|
|
private function extractCommits($html)
|
|
{
|
|
$table = $html->find('table#commits-table', 0);
|
|
$tbody = $table->find('tbody.commit-list', 0);
|
|
|
|
foreach ($tbody->find('tr') as $tr) {
|
|
$item = [];
|
|
|
|
$message = $tr->find('td.message', 0);
|
|
|
|
$item['title'] = $message->find('span.message-wrapper', 0)->plaintext;
|
|
$item['uri'] = $tr->find('td.sha', 0)->find('a', 0)->href;
|
|
$item['author'] = $tr->find('td.author', 0)->plaintext;
|
|
|
|
$var = $tr->find('td', 3);
|
|
$var1 = $var->find('span', 0);
|
|
if ($var1) {
|
|
$item['timestamp'] = $var1->title;
|
|
}
|
|
|
|
if ($message->find('pre.commit-body', 0)) {
|
|
$message->find('pre.commit-body', 0)->style = '';
|
|
|
|
$item['content'] = $message->find('pre.commit-body', 0);
|
|
} else {
|
|
$item['content'] = '<blockquote>' . $item['title'] . '</blockquote>';
|
|
}
|
|
|
|
$this->items[] = $item;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract issues
|
|
*/
|
|
private function extractIssues($html)
|
|
{
|
|
$issueList = $html->find('div#issue-list', 0);
|
|
|
|
foreach ($issueList->find('div.flex-item') as $div) {
|
|
$item = [];
|
|
|
|
$number = trim($div->find('a.index,ml-0.mr-2', 0)->plaintext);
|
|
|
|
$item['title'] = $div->find('a.issue-title', 0)->plaintext . ' (' . $number . ')';
|
|
$item['uri'] = $div->find('a.issue-title', 0)->href;
|
|
|
|
$time = $div->find('relative-time.time-since', 0);
|
|
if ($time) {
|
|
$item['timestamp'] = $time->datetime;
|
|
}
|
|
|
|
//$item['author'] = $li->find('div.desc', 0)->find('a', 1)->plaintext;
|
|
|
|
// Fetch issue page
|
|
$issuePage = getSimpleHTMLDOMCached($item['uri'], 3600);
|
|
$issuePage = defaultLinkTo($issuePage, self::URI);
|
|
|
|
$item['content'] = $issuePage->find('div.timeline-item.comment.first', 0)->find('div.render-content.markup', 0);
|
|
|
|
foreach ($div->find('a.ui.label') as $label) {
|
|
$item['categories'][] = $label->plaintext;
|
|
}
|
|
|
|
$this->items[] = $item;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract issue comments
|
|
*/
|
|
private function extractIssueComments($html)
|
|
{
|
|
$this->issueTitle = $html->find('span#issue-title', 0)->plaintext
|
|
. ' (' . $html->find('span.index', 0)->plaintext . ')';
|
|
|
|
foreach ($html->find('div.timeline-item.comment') as $div) {
|
|
$item = [];
|
|
|
|
if ($div->class === 'timeline-item comment merge box') {
|
|
continue;
|
|
}
|
|
|
|
$item['title'] = $this->ellipsisTitle($div->find('div.render-content.markup', 0)->plaintext);
|
|
$item['uri'] = $div->find('span.text.grey', 0)->find('a', 1)->href;
|
|
$item['content'] = $div->find('div.render-content.markup', 0);
|
|
|
|
if ($div->find('div.dropzone-attachments', 0)) {
|
|
$item['content'] .= $div->find('div.dropzone-attachments', 0);
|
|
}
|
|
|
|
$item['author'] = $div->find('a.author', 0)->innertext;
|
|
|
|
$timeSince = $div->find('span.time-since', 0);
|
|
if ($timeSince) {
|
|
$item['timestamp'] = $timeSince->title;
|
|
}
|
|
|
|
$this->items[] = $item;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract pulls
|
|
*/
|
|
private function extractPulls($html)
|
|
{
|
|
$div = $html->find('div#issue-list', 0);
|
|
|
|
$var2 = $div->find('div.flex-item');
|
|
foreach ($var2 as $li) {
|
|
$item = [];
|
|
|
|
$number = trim($li->find('a.index,ml-0.mr-2', 0)->plaintext);
|
|
|
|
$a = $li->find('a.issue-title', 0);
|
|
$item['title'] = $a->plaintext . ' (' . $number . ')';
|
|
$item['uri'] = $a->href;
|
|
|
|
$time = $li->find('relative-time.time-since', 0);
|
|
if ($time) {
|
|
$item['timestamp'] = $time->datetime;
|
|
}
|
|
|
|
// Extracting the author is a bit awkward after they changed their html
|
|
//$desc = $li->find('div.desc', 0);
|
|
//$item['author'] = $desc->find('a', 1)->plaintext;
|
|
|
|
// Fetch pull request page
|
|
$pullRequestPage = getSimpleHTMLDOMCached($item['uri'], 3600);
|
|
$pullRequestPage = defaultLinkTo($pullRequestPage, self::URI);
|
|
|
|
$var = $pullRequestPage->find('ui.timeline', 0);
|
|
if ($var) {
|
|
$var1 = $var->find('div.render-content.markup', 0);
|
|
$item['content'] = $var1;
|
|
}
|
|
|
|
foreach ($li->find('a.ui.label') as $label) {
|
|
$item['categories'][] = $label->plaintext;
|
|
}
|
|
|
|
$this->items[] = $item;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract releases
|
|
*/
|
|
private function extractReleases($html)
|
|
{
|
|
$ul = $html->find('ul#release-list', 0);
|
|
|
|
$lis = $ul->find('li.ui.grid');
|
|
if ($lis === []) {
|
|
throw new \Exception('Found zero releases');
|
|
}
|
|
foreach ($lis as $li) {
|
|
$item = [];
|
|
$item['title'] = $li->find('h4', 0)->plaintext;
|
|
$item['uri'] = $li->find('h4', 0)->find('a', 0)->href;
|
|
|
|
$tag = $this->stripSvg($li->find('span.tag', 0));
|
|
$commit = $this->stripSvg($li->find('span.commit', 0));
|
|
$downloads = $this->extractDownloads($li->find('details.download', 0));
|
|
|
|
$item['content'] = $li->find('div.markup.desc', 0);
|
|
$item['content'] .= <<<HTML
|
|
<strong>Tag</strong>
|
|
<p>{$tag}</p>
|
|
<strong>Commit</strong>
|
|
<p>{$commit}</p>
|
|
{$downloads}
|
|
HTML;
|
|
|
|
$item['timestamp'] = $li->find('span.time', 0)->find('span', 0)->title;
|
|
$item['author'] = $li->find('span.author', 0)->find('a', 0)->plaintext;
|
|
|
|
$this->items[] = $item;
|
|
}
|
|
}
|
|
|
|
private function extractTags($html)
|
|
{
|
|
$tags = $html->find('td.tag');
|
|
if ($tags === []) {
|
|
throw new \Exception('Found zero tags');
|
|
}
|
|
foreach ($tags as $tag) {
|
|
$this->items[] = [
|
|
'title' => $tag->find('a', 0)->plaintext,
|
|
'uri' => $tag->find('a', 0)->href,
|
|
'content' => $tag->innertext,
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Extract downloads for a releases
|
|
*/
|
|
private function extractDownloads($html, $skipFirst = false)
|
|
{
|
|
$downloads = '';
|
|
|
|
foreach ($html->find('a') as $index => $a) {
|
|
if ($skipFirst === true && $index === 0) {
|
|
continue;
|
|
}
|
|
|
|
$downloads .= <<<HTML
|
|
<a href="{$a->herf}">{$a->plaintext}</a><br>
|
|
HTML;
|
|
}
|
|
|
|
return <<<EOD
|
|
<strong>Downloads</strong>
|
|
<p>{$downloads}</p>
|
|
EOD;
|
|
}
|
|
|
|
/**
|
|
* Ellipsis title to first 100 characters
|
|
*/
|
|
private function ellipsisTitle($text)
|
|
{
|
|
$length = 100;
|
|
|
|
if (strlen($text) > $length) {
|
|
$text = explode('<br>', wordwrap($text, $length, '<br>'));
|
|
return $text[0] . '...';
|
|
}
|
|
return $text;
|
|
}
|
|
|
|
/**
|
|
* Strip SVG tag
|
|
*/
|
|
private function stripSvg($html)
|
|
{
|
|
if ($html === null) {
|
|
return null;
|
|
}
|
|
if ($html->find('svg', 0)) {
|
|
$html->find('svg', 0)->outertext = '';
|
|
}
|
|
|
|
return $html;
|
|
}
|
|
|
|
public function detectParameters($url)
|
|
{
|
|
$params = [];
|
|
|
|
// Issue Comments
|
|
if (preg_match($this->issueCommentsUrlRegex, $url, $matches)) {
|
|
$params['context'] = 'Issue Comments';
|
|
$params['username'] = $matches[1];
|
|
$params['repo'] = $matches[2];
|
|
$params['issueId'] = $matches[3];
|
|
|
|
return $params;
|
|
}
|
|
|
|
// Issues
|
|
if (preg_match($this->issuesUrlRegex, $url, $matches)) {
|
|
$params['context'] = 'Issues';
|
|
$params['username'] = $matches[1];
|
|
$params['repo'] = $matches[2];
|
|
|
|
return $params;
|
|
}
|
|
|
|
// Pull Requests
|
|
if (preg_match($this->pullsUrlRegex, $url, $matches)) {
|
|
$params['context'] = 'Pull Requests';
|
|
$params['username'] = $matches[1];
|
|
$params['repo'] = $matches[2];
|
|
|
|
return $params;
|
|
}
|
|
|
|
// Releases
|
|
if (preg_match($this->releasesUrlRegex, $url, $matches)) {
|
|
$params['context'] = 'Releases';
|
|
$params['username'] = $matches[1];
|
|
$params['repo'] = $matches[2];
|
|
|
|
return $params;
|
|
}
|
|
|
|
// Commits
|
|
if (preg_match($this->urlRegex, $url, $matches)) {
|
|
$params['context'] = 'Commits';
|
|
$params['username'] = $matches[1];
|
|
$params['repo'] = $matches[2];
|
|
|
|
if (isset($matches[3])) {
|
|
$params['branch'] = $matches[3];
|
|
}
|
|
|
|
return $params;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|