Update scripts/

This commit is contained in:
freearhey 2024-12-30 09:33:16 +03:00
parent d9a70f2a1f
commit 2c35704fb0
10 changed files with 282 additions and 0 deletions

View file

@ -0,0 +1,58 @@
import { Logger, Storage, Collection, Dictionary } from '@freearhey/core'
import { IssueLoader, HTMLTable, Markdown } from '../../core'
import { Issue, Site } from '../../models'
import { SITES_DIR, DOT_SITES_DIR } from '../../constants'
import path from 'path'
async function main() {
const logger = new Logger({ disabled: true })
const loader = new IssueLoader()
const storage = new Storage(SITES_DIR)
const sites = new Collection()
logger.info('loading list of sites')
const folders = await storage.list('*/')
logger.info('loading issues...')
const issues = await loadIssues(loader)
logger.info('putting the data together...')
folders.forEach((domain: string) => {
const filteredIssues = issues.filter((issue: Issue) => domain === issue.data.get('site'))
const site = new Site({
domain,
issues: filteredIssues
})
sites.add(site)
})
logger.info('creating sites table...')
let data = new Collection()
sites.forEach((site: Site) => {
data.add([
`<a href="sites/${site.domain}">${site.domain}</a>`,
site.getStatus().emoji,
site.getIssues().all().join(', ')
])
})
const table = new HTMLTable(data.all(), [{ name: 'Site' }, { name: 'Status' }, { name: 'Notes' }])
const readmeStorage = new Storage(DOT_SITES_DIR)
await readmeStorage.save('_table.md', table.toString())
logger.info('updating sites.md...')
const configPath = path.join(DOT_SITES_DIR, 'config.json')
const sitesMarkdown = new Markdown(configPath)
sitesMarkdown.compile()
}
main()
async function loadIssues(loader: IssueLoader) {
const issuesWithStatusWarning = await loader.load({ labels: ['broken guide', 'status:warning'] })
const issuesWithStatusDown = await loader.load({ labels: ['broken guide', 'status:down'] })
return issuesWithStatusWarning.concat(issuesWithStatusDown)
}

View file

@ -2,3 +2,7 @@ export const SITES_DIR = process.env.SITES_DIR || './sites'
export const GUIDES_DIR = process.env.GUIDES_DIR || './guides'
export const DATA_DIR = process.env.DATA_DIR || './temp/data'
export const API_DIR = process.env.API_DIR || '.api'
export const DOT_SITES_DIR = process.env.DOT_SITES_DIR || './.sites'
export const TESTING = process.env.NODE_ENV === 'test' ? true : false
export const OWNER = 'iptv-org'
export const REPO = 'epg'

46
scripts/core/htmlTable.ts Normal file
View file

@ -0,0 +1,46 @@
type Column = {
name: string
nowrap?: boolean
align?: string
}
type DataItem = string[]
export class HTMLTable {
data: DataItem[]
columns: Column[]
constructor(data: DataItem[], columns: Column[]) {
this.data = data
this.columns = columns
}
toString() {
let output = '<table>\n'
output += ' <thead>\n <tr>'
for (const column of this.columns) {
output += `<th align="left">${column.name}</th>`
}
output += '</tr>\n </thead>\n'
output += ' <tbody>\n'
for (const item of this.data) {
output += ' <tr>'
let i = 0
for (const prop in item) {
const column = this.columns[i]
const nowrap = column.nowrap ? ' nowrap' : ''
const align = column.align ? ` align="${column.align}"` : ''
output += `<td${align}${nowrap}>${item[prop]}</td>`
i++
}
output += '</tr>\n'
}
output += ' </tbody>\n'
output += '</table>'
return output
}
}

View file

@ -10,3 +10,7 @@ export * from './guide'
export * from './apiChannel'
export * from './apiClient'
export * from './queueCreator'
export * from './issueLoader'
export * from './issueParser'
export * from './htmlTable'
export * from './markdown'

View file

@ -0,0 +1,40 @@
import { Collection } from '@freearhey/core'
import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods'
import { paginateRest } from '@octokit/plugin-paginate-rest'
import { Octokit } from '@octokit/core'
import { IssueParser } from './'
import { TESTING, OWNER, REPO } from '../constants'
const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods)
const octokit = new CustomOctokit()
export class IssueLoader {
async load({ labels }: { labels: string[] | string }) {
labels = Array.isArray(labels) ? labels.join(',') : labels
let issues: object[] = []
if (TESTING) {
switch (labels) {
case 'broken guide,status:warning':
issues = require('../../tests/__data__/input/issues/broken_guide_warning.js')
break
case 'broken guide,status:down':
issues = require('../../tests/__data__/input/issues/broken_guide_down.js')
break
}
} else {
issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
owner: OWNER,
repo: REPO,
per_page: 100,
labels,
headers: {
'X-GitHub-Api-Version': '2022-11-28'
}
})
}
const parser = new IssueParser()
return new Collection(issues).map(parser.parse)
}
}

View file

@ -0,0 +1,34 @@
import { Dictionary } from '@freearhey/core'
import { Issue } from '../models'
const FIELDS = new Dictionary({
Site: 'site'
})
export class IssueParser {
parse(issue: { number: number; body: string; labels: { name: string }[] }): Issue {
const fields = issue.body.split('###')
const data = new Dictionary()
fields.forEach((field: string) => {
let parsed = field.split(/\r?\n/).filter(Boolean)
let _label = parsed.shift()
_label = _label ? _label.trim() : ''
let _value = parsed.join('\r\n')
_value = _value ? _value.trim() : ''
if (!_label || !_value) return data
const id: string = FIELDS.get(_label)
const value: string = _value === '_No response_' || _value === 'None' ? '' : _value
if (!id) return
data.set(id, value)
})
const labels = issue.labels.map(label => label.name)
return new Issue({ number: issue.number, labels, data })
}
}

13
scripts/core/markdown.ts Normal file
View file

@ -0,0 +1,13 @@
import markdownInclude from 'markdown-include'
export class Markdown {
filepath: string
constructor(filepath: string) {
this.filepath = filepath
}
compile() {
markdownInclude.compileFiles(this.filepath)
}
}

2
scripts/models/index.ts Normal file
View file

@ -0,0 +1,2 @@
export * from './issue'
export * from './site'

24
scripts/models/issue.ts Normal file
View file

@ -0,0 +1,24 @@
import { Dictionary } from '@freearhey/core'
import { OWNER, REPO } from '../constants'
type IssueProps = {
number: number
labels: string[]
data: Dictionary
}
export class Issue {
number: number
labels: string[]
data: Dictionary
constructor({ number, labels, data }: IssueProps) {
this.number = number
this.labels = labels
this.data = data
}
getURL() {
return `https://github.com/${OWNER}/${REPO}/issues/${this.number}`
}
}

57
scripts/models/site.ts Normal file
View file

@ -0,0 +1,57 @@
import { Collection } from '@freearhey/core'
import { Issue } from './'
enum StatusCode {
DOWN = 'down',
WARNING = 'warning',
OK = 'ok'
}
type Status = {
code: StatusCode
emoji: string
}
type SiteProps = {
domain: string
issues: Collection
}
export class Site {
domain: string
issues: Collection
constructor({ domain, issues }: SiteProps) {
this.domain = domain
this.issues = issues
}
getStatus(): Status {
const issuesWithStatusDown = this.issues.filter((issue: Issue) =>
issue.labels.find(label => label === 'status:down')
)
if (issuesWithStatusDown.notEmpty())
return {
code: StatusCode.DOWN,
emoji: '🔴'
}
const issuesWithStatusWarning = this.issues.filter((issue: Issue) =>
issue.labels.find(label => label === 'status:warning')
)
if (issuesWithStatusWarning.notEmpty())
return {
code: StatusCode.WARNING,
emoji: '🟡'
}
return {
code: StatusCode.OK,
emoji: '🟢'
}
}
getIssues(): Collection {
return this.issues.map((issue: Issue) => issue.getURL())
}
}