Skip to content

Commit 12104ce

Browse files
Add MNPD, fix NFD
1 parent b8ef89d commit 12104ce

File tree

4 files changed

+228
-15
lines changed

4 files changed

+228
-15
lines changed

.flox/env/manifest.lock

Lines changed: 99 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.flox/env/manifest.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ version = 1
1717
[install]
1818
nodejs.pkg-path = "nodejs"
1919
ollama.pkg-path = "ollama"
20+
flox-mcp-server.pkg-path = "flox/flox-mcp-server"
21+
flox-mcp-server.systems = ["x86_64-linux", "aarch64-darwin", "aarch64-linux"]
22+
flox-mcp-server.pkg-group = "flox-mcp-server"
2023

2124

2225
## Environment Variables ---------------------------------------------

scripts/mnpd-dispatches.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Description
2+
// MNPD Active Dispatches
3+
//
4+
// Commands
5+
// hubot police - Show all active dispatches
6+
// hubot police <city name> - Filter incidents to a city name
7+
8+
const dayjs = require('dayjs');
9+
const AsciiTable = require('ascii-table');
10+
const relativeTime = require('dayjs/plugin/relativeTime');
11+
const timezone = require('dayjs/plugin/timezone');
12+
const utc = require('dayjs/plugin/utc');
13+
14+
dayjs.extend(relativeTime);
15+
dayjs.extend(timezone);
16+
dayjs.extend(utc);
17+
dayjs.tz.setDefault('America/Chicago');
18+
19+
module.exports = (robot) => {
20+
// Configure dayjs
21+
const baseUrl = 'https://services2.arcgis.com/HdTo6HJqh92wn4D8/arcgis/rest/services/Metro_Nashville_Police_Department_Active_Dispatch_Table_view/FeatureServer/0/query?f=json&cacheHint=true&resultOffset=0&resultRecordCount=50&where=1%3D1&orderByFields=LastUpdated%20ASC%2CObjectId%20ASC&outFields=*&returnGeometry=false&spatialRel=esriSpatialRelIntersects';
22+
23+
const formatTable = (data) => {
24+
const table = new AsciiTable('👮 MNPD Active Dispatches 🚔');
25+
table.setHeading('Time', 'Code', 'Type', 'Location', 'City');
26+
27+
const sortedData = data.features.sort(
28+
(a, b) =>
29+
b.attributes.CallReceivedTime -
30+
a.attributes.CallReceivedTime,
31+
);
32+
33+
sortedData.forEach((row) => {
34+
const { attributes } = row;
35+
36+
table.addRow([
37+
dayjs
38+
.tz(attributes.CallReceivedTime, 'America/Chicago')
39+
.fromNow(),
40+
attributes.IncidentTypeCode,
41+
attributes.IncidentTypeName,
42+
attributes.Location,
43+
attributes.CityName,
44+
]);
45+
});
46+
47+
return table.toString();
48+
};
49+
50+
return robot.respond(/(?:mnpd|👮|police|:cop:)\s?(\d{5})?/i, (msg) => {
51+
const cityName = msg.match[1];
52+
const query = {
53+
where: '1=1',
54+
outFields: '*',
55+
outSR: 4326,
56+
f: 'json',
57+
};
58+
if (cityName) {
59+
query.where = `CityName=${cityName}`;
60+
}
61+
62+
return robot.http(baseUrl)
63+
.query(query)
64+
.get()((err, res, body) => {
65+
const data = JSON.parse(body);
66+
if (data.features?.length === 0) {
67+
msg.send('No active incidents.');
68+
return;
69+
}
70+
71+
const adapterName = robot.adapterName ?? robot.adapter?.name;
72+
if (/slack/.test(adapterName)) {
73+
msg.send(`\`\`\`\n${formatTable(data, msg)}\n\`\`\``);
74+
return;
75+
}
76+
msg.send(formatTable(data, msg));
77+
});
78+
});
79+
};

scripts/nfd-incidents.js

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,53 @@ dayjs.tz.setDefault('America/Chicago');
1818

1919
module.exports = (robot) => {
2020
// Configure dayjs
21-
const baseUrl = 'https://services2.arcgis.com/HdTo6HJqh92wn4D8/arcgis/rest/services/Nashville_Fire_Department_Active_Incidents_view/FeatureServer/0/query?where=1%3D1&objectIds=&time=&resultType=none&outFields=*&returnIdsOnly=false&returnUniqueIdsOnly=false&returnCountOnly=false&returnDistinctValues=false&cacheHint=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&having=&resultOffset=&resultRecordCount=&sqlFormat=none&f=pjson&token=';
21+
const baseUrl = 'https://services2.arcgis.com/HdTo6HJqh92wn4D8/arcgis/rest/services/Nashville_Fire_Department_Active_Incidents_view/FeatureServer/0/query';
2222

2323
const formatTable = (data) => {
24-
const table = new AsciiTable('Nashville Fire Active Incidents');
25-
table.setHeading('Time', 'Postal Code', 'Type', 'Unit Dispatched');
26-
const sortedData = data.features.sort(
27-
(a, b) => b.attributes.DispatchDateTime - a.attributes.DispatchDateTime,
28-
);
29-
sortedData.forEach((row) => {
24+
const table = new AsciiTable('🔥 Nashville Fire Active Incidents 🚒');
25+
table.setHeading('Time', 'Postal Code', 'Type', 'Units Dispatched');
26+
27+
const events = {};
28+
29+
data.features.forEach((row) => {
3030
const { attributes } = row;
31+
const eventId = attributes.event_number;
32+
33+
if (!events[eventId]) {
34+
events[eventId] = {
35+
DispatchDateTime: attributes.DispatchDateTime,
36+
PostalCode: attributes.PostalCode,
37+
incident_type_id: attributes.incident_type_id,
38+
units: new Set(),
39+
};
40+
}
41+
42+
if (attributes.Unit_ID) {
43+
events[eventId].units.add(attributes.Unit_ID);
44+
}
45+
});
46+
47+
const sortedEvents = Object.values(events).sort(
48+
(a, b) => b.DispatchDateTime - a.DispatchDateTime,
49+
);
50+
51+
sortedEvents.forEach((event) => {
52+
const units = Array.from(event.units).sort();
53+
const visibleUnits = units.slice(0, 5);
54+
const remaining = units.length - visibleUnits.length;
55+
3156
table.addRow([
32-
dayjs.tz(attributes.DispatchDateTime, 'America/Chicago').fromNow(),
33-
attributes.PostalCode,
34-
attributes.incident_type_id,
35-
attributes.Unit_ID,
57+
dayjs
58+
.tz(event.DispatchDateTime, 'America/Chicago')
59+
.fromNow(),
60+
event.PostalCode,
61+
event.incident_type_id,
62+
remaining > 0
63+
? `${visibleUnits.join(', ')} + ${remaining} more`
64+
: visibleUnits.join(', '),
3665
]);
3766
});
67+
3868
return table.toString();
3969
};
4070

@@ -43,11 +73,12 @@ module.exports = (robot) => {
4373
const query = {
4474
where: '1=1',
4575
outFields: '*',
46-
outSR: 4326,
47-
f: 'json',
76+
returnGeometry: false,
77+
f: 'pjson',
4878
};
79+
4980
if (zip) {
50-
query.where = `PostalCode=${zip}`;
81+
query.where = `PostalCode='${zip}'`;
5182
}
5283

5384
return robot.http(baseUrl)
@@ -59,7 +90,8 @@ module.exports = (robot) => {
5990
return;
6091
}
6192

62-
if (/slack/.test(robot.adapterName)) {
93+
const adapterName = robot.adapterName ?? robot.adapter?.name;
94+
if (/slack/.test(adapterName)) {
6395
msg.send(`\`\`\`\n${formatTable(data, msg)}\n\`\`\``);
6496
return;
6597
}

0 commit comments

Comments
 (0)