I'm developing a mobile app which is able to record GPS data of an indoor ride like the athlete would circle around a velodrome. It is relatively easy to calculate the GPS points based on the speed measurements the spinning bike provides (compared to an arbitrary GPS route).
The app is uploading my recorded activities in GPX format (gpx.gz to be more precise to speed up things) using the Strava API. The app obtains OAuth token with "activity:write" scope. The Upload POST returns 201 and the upload finishes as well shortly with 200 success code. However after I look at my Strava user dashboard no activity shows up. When I try to view the said activities through Strava's Swagger API play ground it tells me "Record Not Found".
curl -X GET "https://www.strava.com/api/v3/activities/4381960409" -H "accept: application/json" -H "authorization: Bearer zzzzzzzzzzzzzzzzzzzzzzzzzzzz"
https://www.strava.com/api/v3/activities/4381960409
{
"message": "Record Not Found",
"errors": [
{
"resource": "Activity",
"field": "id",
"code": "invalid"
}
]
}
Example activity ids which "got lost in the ether": 4381670165, 4381744693, 4381960409.
My problem is that I don't have any debug information about what could be wrong. I receive success codes, but then the activities just never really materialize. Furthermore I cannot check the Upload's status through their Swagger, because the OAuth token there only has read privileges.
Since I'm generating the GPX files I tested them by manually uploading them. The first one as a Virtual Ride (https://www.strava.com/activities/4094942758) and the second one as a Ride tagged as Indoor cycling (https://www.strava.com/activities/4094974788). Neither of them shows any GPS data whatsoever. However the file contains the data.
So maybe the GPX files have some problem? Here are the two: https://drive.google.com/drive/folders/1dkUvrLxW2r3tvQqvoqAkOB9998N9uLn7?usp=sharing
The app is written in Flutter and uses my derivatives of strava_flutter and rw_tcx.
final stravaService = Get.find<StravaService>();
await stravaService.login();
final records = await _database.recordDao.findAllActivityRecords(activity.id);
final statusCode = await stravaService.upload(activity, records);
if (statusCode == statusOk) {
activity.uploaded = true;
await _database.activityDao.updateActivity(activity);
}
As I mention, it completes successfully, both the Upload POST and then it gets back 200, this is in the guts of strava_flutter.
I made multiple mistakes:
I was dealing with multiple file formats (FIT, GPX, TCX) and the actual file I uploaded was TCX. Kudos to Strava developers the system was able to gracefully swallow that and extract the info from the TCX, didn't return an error code. That's smooth.
The main reason why the GPS didn't show: I swapped the lat-lon coordinates. Unfortunately that can be confusing as well, especially if someone is tired. https://macwright.com/lonlat/ After some additional corrections the GPS now shows: https://www.strava.com/activities/4104607928
Related
Ok, I am grabbing data from a user using graph api (Power Automate). The URL i am using is:
GET https://graph.microsoft.com/v1.0/users/{id}/manageddevices
This produces a good list. It has the ID, azureID, and other items. The client wants me to delete this device from azure AD. Here is the delete code I am trying to use in my loop.
DELETE https://graph.microsoft.com/v1.0/devices/{The first commands ID}
However, the ID is not matching. The first command's ID shows device 1BB373, but Azure AD shows the device id 855 and the object id as 8b737.
I have also tried using the delete managed device as this device is in intune.
DELETE /users/{usersId}/managedDevices/{The first commands ID}
DELETE /deviceManagement/managedDevices/{The first commands ID}
The device is azure domain joined by the user in question. This will also be applied to non-windows devices as well like cellphones and tablets. Am I on the right track, do I need to change up how I am gathering the users devices?
Update:
I tried:
Delete https://graph.microsoft.com/v1.0/users/{ID}/managedevices/{ID}
I received a different result this time around. Here is the error message:
"code": "No method match route template",
"message": "No OData route exists that match template ~/entityset/key/navigation/key with http verb DELETE for request /DeviceFE/StatelessDeviceFEService/users('4c1d-userid')/managedDevices('99ed-id').",
I'm not sure what this all means. (Delete ManagedDevices) The permissions are set according to this documnetation.
Update 4/6/2022:
I have discovered that the DeviceID and the AzureID are the same.
Thus I can use the
GET /devices?$search="deviceID=AzureID
I have tried be above methodologies and discovered the Delete /devices does not have an application method. Here is the method I finally tried that gave me any kind of response:
GET https://graph.microsoft.com/v1.0/users/#{items('select_loop')['id']}/managedDevices/#{items('ATE-DeleteDevice')}
This was the response, ids shortened of course
{
"error": {
"code": "No method match route template",
"message": "No OData route exists that match template ~/entityset/key/navigation/key with http verb DELETE for request /DeviceFE/StatelessDeviceFEService/users('4c1d35d0')/managedDevices('{\"id\":\"8b737697\"}').",
"innerError": {
"date": "2022-04-06T17:08:47",
"request-id": "1d57a423",
"client-request-id": "1d57a423"
}
}
}
Deleting a device from Azure AD is currently in preview for Graph API Beta.
DELETE /admin/windows/updates/updatableAssets/{azureADDeviceId}
Graph API v1.0 doesn't support deleting a device from Azure AD.
Resources:
Delete azureADDevice
I've been poking around at the YouTube live chat API to render out a custom chat feed, and was wondering how I can show membership/sponsorship badges next to users like the YouTube site itself does?
Looking at a response from the API, I can see that YouTube does tell me the user is a member/sponsor, but it doesn't include at what level/duration nor what badge image should be shown:
{
"kind": "youtube#liveChatMessage",
"etag": "MHpDf4piJnYR2X3lP-7mwBavfWM",
"id": "LCC.CjgKDQoLd1VwYUIzYTdkVW8qJwoYVUNEWExPVjNTMEdUd21EOFY4R1A2dzlREgt3VXBhQjNhN2RVbxI7ChpDSVRodDQzS292VUNGZVV0clFZZHNJRUwzZxIdQ1B1VHJiYV9vdlVDRllhRGdnb2RaUE1LanctMjY",
"snippet": {
"type": "textMessageEvent",
"liveChatId": "Cg0KC3dVcGFCM2E3ZFVvKicKGFVDRFhMT1YzUzBHVHdtRDhWOEdQNnc5URILd1VwYUIzYTdkVW8",
"authorChannelId": "UCYC1zf9Dznp-xpe9rwEopLQ",
"publishedAt": "2022-01-08T16:31:12.317Z",
"hasDisplayContent": true,
"displayMessage": "Instead of waiting 30 seconds you had to spam facecam now you get a 5 minute timeout",
"textMessageDetails": {
"messageText": "Instead of waiting 30 seconds you had to spam facecam now you get a 5 minute timeout"
}
},
"authorDetails": {
"channelId": "UCYC1zf9Dznp-xpe9rwEopLQ",
"channelUrl": "http://www.youtube.com/channel/UCYC1zf9Dznp-xpe9rwEopLQ",
"displayName": "Cody Kerley",
"profileImageUrl": "https://yt3.ggpht.com/ytc/AKedOLQFiwv-x6ukfTOh7pD7WlCe7Ss1AB5wH7QAF53uiQ=s88-c-k-c0x00ffffff-no-rj",
"isVerified": false,
"isChatOwner": false,
"isChatSponsor": true,
"isChatModerator": true
}
}
But if I look at how this message was shown in the YouTube chat itself, the user has the correct membership badge for their level/duration, specific to this channel, and the tooltip also shows you the level/duration of the membership/sponsorship:
How can I get this information from the API for each chat message so that I can render the badge correctly myself?
Cheers.
As you said there doesn't seem to be any official YouTube Data API v3 endpoint providing membership badges for YouTube live chat messages.
However I reverse-engineered YouTube live chat messages and here is the solution:
Get a continuation token starting with 0ofMyAO (there seems to be 2 that both work) by executing (don't forget to change VIDEO_ID with your YouTube live video id):
curl -s 'https://www.youtube.com/live_chat?v=VIDEO_ID' -H 'User-Agent: Firefox/99'
Use this continuation token to fetch all pieces of information about current YouTube live chat messages by executing (don't forget to change CONTINUATION_TOKEN with the continuation token you grab at step 1., note: don't care about the key it's not a YouTube Data API v3 key):
curl -s 'https://www.youtube.com/youtubei/v1/live_chat/get_live_chat?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8' -H 'Content-Type: application/json' --data-raw '{"context":{"client":{"clientName":"WEB","clientVersion":"2.9999099"}},"continuation":"CONTINUATION_TOKEN"}'
Likewise you'll get all pieces of information about current YouTube live chat messages since the moment you grab the continuation token at step 1. however continuation token seems to expire every 5 minutes so grab a new one from the response of step 2. or by doing step 1. once again.
Note 0: during the 5 minutes window, you can execute step 2. as many time as you want to get messages in real time
Note 1: I recommend you to change the continuation token every 4 minutes in order not to miss any message
I let you understand the JSON response that contain the pieces of information you are looking for
Note: at step 1. you retrieve recent message sent before your request but it's in HTML format and not JSON this time
When I tried to retrieve a Photo using Graph Explorer, it worked well using the sample account.
But when I sign in with my own account, it was broken.
When I request:
https://graph.microsoft.com/v1.0/me/contacts/{Id}/photo/$value
It returnes:
{
"error": {
"code": "ErrorItemNotFound",
"message": "The specified object was not found in the store.",
"innerError": {
"request-id": "ad665ca9-9585-4f7a-9dd1-803061d8baba",
"date": "2018-10-09T15:55:54"
}
}
}
I don't know the reason, it confused me.
Calling /v1.0/me/contacts/{id}/photo/$value will only return a photo is one exists (i.e. a photo was added to the Contact in Outlook). If no photo was added, it will return an HTTP 404 - Not Found exception.
Rather than handling the exception, you can test for the existence of a photo by calling /v1.0/me/contacts/{id}/photo/ first. If a photo was attached. this will return the metadata for the attached photo (mediaContentType, height, width). If the results come back empty then you know there isn't a photo available.
Keep in mind that Outlook itself pulls profile photos from multiple places. For example, if there isn't an image attached to a Contact and they're part of the same organization, Outlook will attempt to pull the photo from the Contact's account. You can replicate this functionality so long as you have requested the scope User.ReadBasic.All.
Together, the process would be something like this:
Call /v1.0/me/contacts/{id}/photo/
If an image exists, call /v1.0/me/contacts/{id}/photo/$value to retrieve the image. If not, continue on.
Using the emailAddress from the Contact, call /v1.0/users/{emailAddress}/photo
If an image exists, call /v1.0/users/{emailAddress}/photo/$value to retrieve the image. If not, there isn't an image available.
As the Dialogflow documentations states, the data field represents
Additional data required for performing the action on the client side.
The data is sent to the client in the original form and is not
processed by Dialogflow.
How should one access it in the iOS framework?
request?.setMappedCompletionBlockSuccess({ (request, response) in
...
}
I couldn't find it in the response object and can't find any documentation for iOS.
Thanks.
Your question is a bit vague (can you edit and narrow it down?), but i think you got it the other way round, what that snippet of documentation that you pasted means is that you are supposed to send that payload to DialogFlow and it will forward it to a connected Client (e.g Messenger, Slack etc) un-touched. It simply means that DialogFlow assumes that you know what you are doing.
Here is a sample Fulfilment response to DialogFlow in JS
module.exports.sendGenericMessageWithText = function(message) {
return {
data: {
facebook: [
{
text: message
]
}
}
}
I want to get notified when I enter in roaming area in my iOS app, I have already read the documentation for NSLocale , SCNetworkReachability , and core telephony (I may have missed something). I need to get this info from sim (or any other way if possible).
The usual method would be to get the carrier's country code from the core telephony interface and then compare that with the country code from reverse geocoding the location.
Advantages: works with VPNs and when the user has disabled data when roaming.
Disadvantages: doesn't work without location.
I don't have any non-copyright code for you, but the key you need in the place marks dictionary you need for country code is #"CountryCode"
Geocoding would be something like:-
CLGeocoder* geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler: ^(NSArray* placemarks){}]
The country code for the provider would be
NSString* homeCountry = [netInfo.subscriberCellularProvider isoCountryCode];
Hope this helps
There's no iOS API for detecting roaming status, but you can use third party services like http://ipinfo.io (my own service) to find out the current country of even carrier code based on the device's IP address. You can then compare that to the CTCarrier details to determine if the device is roaming. Here's the standard ipinfo.io API response:
$ curl ipinfo.io/24.32.148.1
{
"ip": "24.32.148.1",
"hostname": "doc-24-32-148-1.pecos.tx.cebridge.net",
"city": "Pecos",
"region": "Texas",
"country": "US",
"loc": "31.3086,-103.5892",
"org": "AS7018 AT&T Services, Inc.",
"postal": "79772"
}
Custom packages are available that also include the mnc/mcc details of mobile IPs though. See http://ipinfo.io/developers for details.
We used to have the same problem before on iOS and we ended up building a dedicated API for it. It works by comparing the IP Geolocation (based on IP address of the requester party) of the device against it's GPS position provided. If the user is detected as being physically outside of the country determined by their IP address, then they are deemed to be roaming.
We decided to offer this API for free and unlimited, no restrictions, no throttling. No credit card, not even account required, just run a simple query:
curl -X GET --header 'Accept: application/json' 'https://api.bigdatacloud.net/data/am-i-roaming?latitude=[your latitude]&longitude=[your longitude]'
the response is very simple, just a true or false:
{
"isRoaming": true
}
It's very fast too! Our servers usually respond in under 1 millisecond time.
This API can obviously give a false positive results if executed via VPN/proxy or a non cellular interface, therefore it would be suggested to make sure you're using cellular interface when making the call.
Enjoy!