Skip to content

Commit 22795a4

Browse files
Merge pull request #62 from squaredup/work/tw/worldcup2026-plugin
Add FIFA World Cup 2026 plugin
2 parents efb12f9 + d34f994 commit 22795a4

25 files changed

Lines changed: 743 additions & 0 deletions

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ plugins/SumoLogic/* @richpeters
2323
plugins/TransportForLondon/* @clarkd
2424
plugins/UniFi/* @adamkinniburgh
2525
plugins/UptimeRobot/* @kieranlangton
26+
plugins/WorldCup2026/* @TimWheeler-SQUP
2627

2728

2829
# Fallback – if a plugin has no specified author
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"steps": [
3+
{
4+
"displayName": "Connect to openfootball",
5+
"dataStream": { "name": "worldcup2026-teams-import" },
6+
"required": true,
7+
"error": "Could not reach the openfootball data source. Check your network connection.",
8+
"success": "Connected successfully."
9+
}
10+
]
11+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[
2+
{
3+
"name": "World Cup Team",
4+
"sourceType": "World Cup Team",
5+
"icon": "flag",
6+
"singular": "Team",
7+
"plural": "Teams"
8+
}
9+
]
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
var matches = data.matches || [];
2+
var teamFilter = context.objects[0] ? [].concat(context.objects[0].teamName)[0] || '' : '';
3+
4+
var groupMatches = matches.filter(function(m) { return m.group; });
5+
6+
var standings = {};
7+
8+
function ensureTeam(name, group) {
9+
if (!standings[name]) {
10+
standings[name] = { group: group, mp: 0, w: 0, d: 0, l: 0, gf: 0, ga: 0, pts: 0 };
11+
}
12+
}
13+
14+
groupMatches.forEach(function(m) {
15+
ensureTeam(m.team1, m.group);
16+
ensureTeam(m.team2, m.group);
17+
18+
if (!m.score || !m.score.ft) return;
19+
20+
var s1 = m.score.ft[0];
21+
var s2 = m.score.ft[1];
22+
var t1 = standings[m.team1];
23+
var t2 = standings[m.team2];
24+
25+
t1.mp++; t2.mp++;
26+
t1.gf += s1; t1.ga += s2;
27+
t2.gf += s2; t2.ga += s1;
28+
29+
if (s1 > s2) {
30+
t1.w++; t1.pts += 3; t2.l++;
31+
} else if (s2 > s1) {
32+
t2.w++; t2.pts += 3; t1.l++;
33+
} else {
34+
t1.d++; t1.pts++; t2.d++; t2.pts++;
35+
}
36+
});
37+
38+
var rows = Object.keys(standings).map(function(name) {
39+
var s = standings[name];
40+
return {
41+
sourceId: name,
42+
team: name,
43+
group: s.group,
44+
mp: s.mp, w: s.w, d: s.d, l: s.l,
45+
gf: s.gf, ga: s.ga, gd: s.gf - s.ga, pts: s.pts
46+
};
47+
});
48+
49+
if (teamFilter && standings[teamFilter]) {
50+
var targetGroup = standings[teamFilter].group;
51+
rows = rows.filter(function(r) { return r.group === targetGroup; });
52+
}
53+
54+
rows.sort(function(a, b) {
55+
if (a.group !== b.group) return a.group.localeCompare(b.group);
56+
if (b.pts !== a.pts) return b.pts - a.pts;
57+
if (b.gd !== a.gd) return b.gd - a.gd;
58+
return b.gf - a.gf;
59+
});
60+
61+
result = rows;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
var matches = data.matches || [];
2+
3+
var stageMap = {
4+
'Round of 32': 'Round of 32',
5+
'Round of 16': 'Round of 16',
6+
'Quarter-final': 'Quarter-Final',
7+
'Semi-final': 'Semi-Final',
8+
'Third-place play-off': 'Third Place',
9+
'Final': 'Final'
10+
};
11+
12+
result = matches.filter(function(m) { return !m.group; }).map(function(m) {
13+
var score = '-';
14+
var status = 'Upcoming';
15+
if (m.score && m.score.ft) {
16+
score = m.score.ft[0] + '-' + m.score.ft[1];
17+
status = 'Finished';
18+
}
19+
20+
return {
21+
date: m.date,
22+
round: stageMap[m.round] || m.round,
23+
home_team: m.team1,
24+
away_team: m.team2,
25+
score: score,
26+
status: status
27+
};
28+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
var matches = data.matches || [];
2+
var teamName = context.objects[0] ? [].concat(context.objects[0].teamName)[0] || '' : '';
3+
var sourceId = context.objects[0] ? context.objects[0].sourceId : '';
4+
5+
var knockoutStageMap = {
6+
'Round of 32': 'Round of 32',
7+
'Round of 16': 'Round of 16',
8+
'Quarter-final': 'Quarter-Final',
9+
'Semi-final': 'Semi-Final',
10+
'Third-place play-off': 'Third Place',
11+
'Final': 'Final'
12+
};
13+
14+
var played = matches.filter(function(m) {
15+
return m.score && (m.team1 === teamName || m.team2 === teamName);
16+
});
17+
18+
played.sort(function(a, b) {
19+
return new Date(b.date) - new Date(a.date);
20+
});
21+
22+
if (played.length === 0) {
23+
result = [{ date: 'No matches played yet', home_away: '', opponent: '', score: '', result: '', stage: '', sourceId: sourceId }];
24+
} else {
25+
var last = played[0];
26+
var isHome = last.team1 === teamName;
27+
var opponent = isHome ? last.team2 : last.team1;
28+
var myScore = isHome ? last.score.ft[0] : last.score.ft[1];
29+
var oppScore = isHome ? last.score.ft[1] : last.score.ft[0];
30+
var matchResult = myScore > oppScore ? 'Win' : myScore < oppScore ? 'Loss' : 'Draw';
31+
32+
result = [{
33+
date: last.date,
34+
home_away: isHome ? 'Home' : 'Away',
35+
opponent: opponent,
36+
score: myScore + '-' + oppScore,
37+
result: matchResult,
38+
stage: last.group ? 'Group Stage' : (knockoutStageMap[last.round] || last.round),
39+
sourceId: sourceId
40+
}];
41+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
var matches = data.matches || [];
2+
var teamName = context.objects[0] ? [].concat(context.objects[0].teamName)[0] || '' : '';
3+
4+
var knockoutStageMap = {
5+
'Round of 32': 'Round of 32',
6+
'Round of 16': 'Round of 16',
7+
'Quarter-final': 'Quarter-Final',
8+
'Semi-final': 'Semi-Final',
9+
'Third-place play-off': 'Third Place',
10+
'Final': 'Final'
11+
};
12+
13+
var filtered = matches.filter(function(m) {
14+
return m.team1 === teamName || m.team2 === teamName;
15+
});
16+
17+
filtered.sort(function(a, b) {
18+
return new Date(a.date) - new Date(b.date);
19+
});
20+
21+
result = filtered.map(function(m) {
22+
var score = '-';
23+
var status = 'Upcoming';
24+
if (m.score && m.score.ft) {
25+
score = m.score.ft[0] + '-' + m.score.ft[1];
26+
status = 'Finished';
27+
}
28+
29+
return {
30+
date: m.date,
31+
home_team: m.team1,
32+
away_team: m.team2,
33+
score: score,
34+
group: m.group || '',
35+
stage: m.group ? 'Group Stage' : (knockoutStageMap[m.round] || m.round),
36+
status: status
37+
};
38+
});
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
var matches = data.matches || [];
2+
var teamName = context.objects[0] ? [].concat(context.objects[0].teamName)[0] || '' : '';
3+
var sourceId = context.objects[0] ? context.objects[0].sourceId : '';
4+
5+
var knockoutStageMap = {
6+
'Round of 32': 'Round of 32',
7+
'Round of 16': 'Round of 16',
8+
'Quarter-final': 'Quarter-Final',
9+
'Semi-final': 'Semi-Final',
10+
'Third-place play-off': 'Third Place',
11+
'Final': 'Final'
12+
};
13+
14+
var upcoming = matches.filter(function(m) {
15+
return !m.score && (m.team1 === teamName || m.team2 === teamName);
16+
});
17+
18+
upcoming.sort(function(a, b) {
19+
return new Date(a.date) - new Date(b.date);
20+
});
21+
22+
if (upcoming.length === 0) {
23+
result = [{ date: 'No upcoming matches', home_away: '', opponent: '', stage: '', group: '', sourceId: sourceId }];
24+
} else {
25+
var next = upcoming[0];
26+
var isHome = next.team1 === teamName;
27+
var opponent = isHome ? next.team2 : next.team1;
28+
result = [{
29+
date: next.date,
30+
home_away: isHome ? 'Home' : 'Away',
31+
opponent: opponent,
32+
stage: next.group ? 'Group Stage' : (knockoutStageMap[next.round] || next.round),
33+
group: next.group || '',
34+
sourceId: sourceId
35+
}];
36+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
var matches = data.matches || [];
2+
var teamName = context.objects[0] ? [].concat(context.objects[0].teamName)[0] || '' : '';
3+
var group = context.objects[0] ? [].concat(context.objects[0].group)[0] || '' : '';
4+
5+
var groupGames = matches.filter(function(m) {
6+
return m.group && (m.team1 === teamName || m.team2 === teamName);
7+
});
8+
9+
var mp = 0, w = 0, d = 0, l = 0, gf = 0, ga = 0;
10+
11+
groupGames.forEach(function(m) {
12+
if (!m.score || !m.score.ft) return;
13+
mp++;
14+
var isHome = m.team1 === teamName;
15+
var myScore = isHome ? m.score.ft[0] : m.score.ft[1];
16+
var oppScore = isHome ? m.score.ft[1] : m.score.ft[0];
17+
gf += myScore;
18+
ga += oppScore;
19+
if (myScore > oppScore) w++;
20+
else if (myScore < oppScore) l++;
21+
else d++;
22+
});
23+
24+
var pts = (w * 3) + d;
25+
var gd = gf - ga;
26+
27+
result = [{ country: teamName, group: group, mp: mp, w: w, d: d, l: l, pts: pts, gf: gf, ga: ga, gd: gd, sourceId: teamName }];
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
var matches = data.matches || [];
2+
var seen = {};
3+
var teams = [];
4+
5+
matches.filter(function(m) { return m.group; }).forEach(function(m) {
6+
[{ name: m.team1, group: m.group }, { name: m.team2, group: m.group }].forEach(function(t) {
7+
if (!seen[t.name]) {
8+
seen[t.name] = true;
9+
teams.push({ sourceId: t.name, name_en: t.name, group: t.group });
10+
}
11+
});
12+
});
13+
14+
result = teams;

0 commit comments

Comments
 (0)