mirror of
https://github.com/iptv-org/epg.git
synced 2025-05-09 08:30:06 -04:00
Update scripts/
This commit is contained in:
parent
d9a70f2a1f
commit
2c35704fb0
10 changed files with 282 additions and 0 deletions
58
scripts/commands/sites/update.ts
Normal file
58
scripts/commands/sites/update.ts
Normal 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)
|
||||||
|
}
|
|
@ -2,3 +2,7 @@ export const SITES_DIR = process.env.SITES_DIR || './sites'
|
||||||
export const GUIDES_DIR = process.env.GUIDES_DIR || './guides'
|
export const GUIDES_DIR = process.env.GUIDES_DIR || './guides'
|
||||||
export const DATA_DIR = process.env.DATA_DIR || './temp/data'
|
export const DATA_DIR = process.env.DATA_DIR || './temp/data'
|
||||||
export const API_DIR = process.env.API_DIR || '.api'
|
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
46
scripts/core/htmlTable.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,3 +10,7 @@ export * from './guide'
|
||||||
export * from './apiChannel'
|
export * from './apiChannel'
|
||||||
export * from './apiClient'
|
export * from './apiClient'
|
||||||
export * from './queueCreator'
|
export * from './queueCreator'
|
||||||
|
export * from './issueLoader'
|
||||||
|
export * from './issueParser'
|
||||||
|
export * from './htmlTable'
|
||||||
|
export * from './markdown'
|
||||||
|
|
40
scripts/core/issueLoader.ts
Normal file
40
scripts/core/issueLoader.ts
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
34
scripts/core/issueParser.ts
Normal file
34
scripts/core/issueParser.ts
Normal 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
13
scripts/core/markdown.ts
Normal 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
2
scripts/models/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './issue'
|
||||||
|
export * from './site'
|
24
scripts/models/issue.ts
Normal file
24
scripts/models/issue.ts
Normal 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
57
scripts/models/site.ts
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue