hubot-nextbus 2.2.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/nodejs.yml +1 -2
- package/.github/workflows/npm-publish.yml +31 -6
- package/package.json +1 -1
- package/src/nextbus.js +77 -13
- package/test/adapters/slack.js +5 -0
- package/test/fixtures/stops-BRO12WN-next.json +2312 -0
- package/test/fixtures/stops-PORGRESF-next.json +1750 -0
- package/test/fixtures/stops-near-gps.json +51 -51
- package/test/nextbus-slack.test.js +147 -0
- package/test/{nextbus_test.js → nextbus.test.js} +53 -44
- package/test/fixtures/stops-CHA7AWN-trips.json +0 -277720
|
@@ -1,26 +1,51 @@
|
|
|
1
1
|
on:
|
|
2
2
|
push:
|
|
3
|
-
branches:
|
|
3
|
+
branches:
|
|
4
|
+
- main
|
|
4
5
|
|
|
5
6
|
name: npm-publish
|
|
6
7
|
|
|
7
8
|
permissions:
|
|
8
9
|
contents: write
|
|
10
|
+
id-token: write
|
|
9
11
|
|
|
10
12
|
jobs:
|
|
11
13
|
publish:
|
|
12
14
|
runs-on: ubuntu-latest
|
|
13
15
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
15
|
-
|
|
16
|
+
- uses: actions/checkout@v6
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-node@v6
|
|
16
19
|
with:
|
|
17
20
|
node-version: "20"
|
|
21
|
+
registry-url: https://registry.npmjs.org
|
|
22
|
+
|
|
23
|
+
- name: Update npm to latest version
|
|
24
|
+
run: npm install -g npm@latest
|
|
25
|
+
|
|
18
26
|
- run: npm ci
|
|
19
27
|
- run: npm test
|
|
20
|
-
|
|
28
|
+
|
|
29
|
+
- name: Publish to npm
|
|
21
30
|
id: publish
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
run: |
|
|
32
|
+
# Verify npm version supports OIDC
|
|
33
|
+
echo "npm version: $(npm --version)"
|
|
34
|
+
|
|
35
|
+
# Check if version is already published
|
|
36
|
+
PACKAGE_NAME=$(node -p "require('./package.json').name")
|
|
37
|
+
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
|
38
|
+
|
|
39
|
+
if npm view "$PACKAGE_NAME@$PACKAGE_VERSION" version 2>/dev/null; then
|
|
40
|
+
echo "Version $PACKAGE_VERSION already published"
|
|
41
|
+
echo "type=" >> $GITHUB_OUTPUT
|
|
42
|
+
else
|
|
43
|
+
echo "Publishing $PACKAGE_NAME@$PACKAGE_VERSION..."
|
|
44
|
+
npm publish --access public
|
|
45
|
+
echo "type=patch" >> $GITHUB_OUTPUT
|
|
46
|
+
echo "version=$PACKAGE_VERSION" >> $GITHUB_OUTPUT
|
|
47
|
+
fi
|
|
48
|
+
|
|
24
49
|
- if: ${{ steps.publish.outputs.type }}
|
|
25
50
|
name: Create Release
|
|
26
51
|
env:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hubot-nextbus",
|
|
3
3
|
"description": "Shows when the next transit vehicle will arrive at a particular stop.",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.3.0",
|
|
5
5
|
"author": "Stephen Yeargin <stephen@yearg.in>",
|
|
6
6
|
"homepage": "https://github.com/transitnownash/hubot-nextbus",
|
|
7
7
|
"license": "MIT",
|
package/src/nextbus.js
CHANGED
|
@@ -53,33 +53,97 @@ module.exports = (robot) => {
|
|
|
53
53
|
return moment(`${moment().format('YYYY-MM-DD')} ${timeStr.trim().padStart(8, '0')}`);
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
+
const getRealTimeStatus = (stopTime) => {
|
|
57
|
+
// Check if realtime data is available
|
|
58
|
+
if (!stopTime.realtime || !stopTime.realtime.departure) {
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const scheduled = formatTripTimeAsMoment(stopTime.departure_time);
|
|
63
|
+
const actual = formatTripTimeAsMoment(stopTime.realtime.departure);
|
|
64
|
+
const diffMinutes = actual.diff(scheduled, 'minutes');
|
|
65
|
+
|
|
66
|
+
if (diffMinutes === 0) {
|
|
67
|
+
return 'On time';
|
|
68
|
+
} if (diffMinutes > 0) {
|
|
69
|
+
return `${diffMinutes}m late`;
|
|
70
|
+
}
|
|
71
|
+
return `${Math.abs(diffMinutes)}m early`;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const formatAlerts = (alerts) => {
|
|
75
|
+
if (!alerts || alerts.length === 0) {
|
|
76
|
+
return '';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const alertLines = alerts.map((alert) => {
|
|
80
|
+
const headerText = alert.header_text?.translation?.[0]?.text || 'Service Alert';
|
|
81
|
+
return `⚠️ *${headerText.trim()}*`;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return alertLines.join('\n');
|
|
85
|
+
};
|
|
86
|
+
|
|
56
87
|
const queryStopById = (stopId, msg) => getAPIResponse('agencies.json', msg, (agencies) => {
|
|
57
88
|
// Override timezone for moment() calls
|
|
58
89
|
process.env.TZ = agencies.data[0].agency_timezone;
|
|
59
90
|
robot.logger.debug(process.env.TZ);
|
|
60
91
|
robot.logger.debug('Current Time:', moment());
|
|
61
|
-
getAPIResponse(`stops/${stopId}/
|
|
62
|
-
const
|
|
63
|
-
|
|
92
|
+
getAPIResponse(`stops/${stopId}/next.json`, msg, (response) => {
|
|
93
|
+
const {
|
|
94
|
+
stop,
|
|
95
|
+
next_trip: nextTrip,
|
|
96
|
+
upcoming_trips: upcomingTrips,
|
|
97
|
+
alerts,
|
|
98
|
+
vehicle_positions: vehiclePositions,
|
|
99
|
+
} = response;
|
|
100
|
+
|
|
101
|
+
// Show alerts if any exist
|
|
102
|
+
const alertMessage = formatAlerts(alerts);
|
|
103
|
+
if (alertMessage) {
|
|
104
|
+
msg.send(alertMessage);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Combine next trip with upcoming trips to get all trips
|
|
108
|
+
const allTrips = nextTrip ? [nextTrip, ...upcomingTrips] : upcomingTrips;
|
|
109
|
+
|
|
110
|
+
const nextTripsData = allTrips.filter((tripData) => {
|
|
111
|
+
const tripTime = formatTripTimeAsMoment(tripData.stop_time.arrival_time);
|
|
64
112
|
return tripTime.isAfter(moment(), 'second');
|
|
65
113
|
});
|
|
66
|
-
|
|
67
|
-
|
|
114
|
+
|
|
115
|
+
robot.logger.debug(nextTripsData);
|
|
116
|
+
if (nextTripsData.length === 0) {
|
|
68
117
|
msg.send('The last bus has already run for today.');
|
|
69
118
|
return;
|
|
70
119
|
}
|
|
71
120
|
|
|
72
121
|
const table = new AsciiTable();
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
122
|
+
nextTripsData.slice(0, 5).forEach((tripData) => {
|
|
123
|
+
const tripTime = formatTripTimeAsMoment(tripData.stop_time.arrival_time);
|
|
124
|
+
const realtimeStatus = getRealTimeStatus(tripData.stop_time);
|
|
125
|
+
const hasVehicle = vehiclePositions
|
|
126
|
+
&& vehiclePositions.some((vp) => vp.trip && vp.trip.trip_id === tripData.trip.trip_gid);
|
|
127
|
+
const busIndicator = hasVehicle ? ' 🚌' : '';
|
|
128
|
+
const timeUntilText = realtimeStatus ? `${tripTime.fromNow()} (${realtimeStatus})` : tripTime.fromNow();
|
|
129
|
+
const columns = [
|
|
130
|
+
formatTripTimeAsMoment(tripData.stop_time.arrival_time).format('LT'),
|
|
131
|
+
`#${tripData.trip.route_gid} - ${tripData.trip.trip_headsign}${busIndicator}`,
|
|
132
|
+
timeUntilText,
|
|
133
|
+
];
|
|
134
|
+
table.addRow(columns);
|
|
80
135
|
});
|
|
136
|
+
const adapterName = robot.adapterName ?? robot.adapter?.name ?? '';
|
|
81
137
|
table.removeBorder();
|
|
82
|
-
|
|
138
|
+
const tableOutput = table.toString().split('\n').map((line) => line.trimEnd()).join('\n');
|
|
139
|
+
|
|
140
|
+
const heading = `🚏 *${stop.stop_name}*`;
|
|
141
|
+
|
|
142
|
+
if (/slack/i.test(adapterName)) {
|
|
143
|
+
msg.send(`${heading}\n\`\`\`\n${tableOutput}\n\`\`\``);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
msg.send(`${heading}\n${tableOutput}`);
|
|
83
147
|
});
|
|
84
148
|
});
|
|
85
149
|
|