sbabcock23 / pytryfi

Apache License 2.0
31 stars 10 forks source link

Activity Logs #10

Open jmargel74 opened 2 years ago

jmargel74 commented 2 years ago

Using your query to create a .net application. Thank you for your hard work! As for the activity section I believe that's coming from a site: https://analytics.tryfi.com

It looks like it's on a aws system and having issues getting through. Giving me a {"message":"Missing Authentication Token"}. Tried the session_id but no luck. I don't believe it's a graphql query for the analytics (pet ranking, etc..)

sbabcock23 commented 2 years ago

I believe it's all graphql even for the activity tracker.

cwille97 commented 1 year ago

You can MITM your own app traffic and get a decent idea of what is happening here. That analytics subdomain is for app analytics, it looks like the activity logs are on the main api subdomain. Interestingly, logs are also submitted to the api subdomain under /api/log/submit which makes me think maybe the analytics subdomain is a 3rd party analytics provider that collects that data and provides insights to Fi.

Here is a sample request for the activity feed:

POST https://api.tryfi.com/graphql HTTP/2.0
content-type: application/json
x-apollo-operation-name: FiFeed
accept: */*
x-session-id: redacted
apollographql-client-version: 3.0.5-0
accept-language: en-US,en;q=0.9
accept-encoding: gzip, deflate, br
x-acting-pet-id: redacted
content-length: redacted
x-apollo-operation-type: query
user-agent: Fi/0 CFNetwork/1402.0.8 Darwin/22.2.0
apollographql-client-name: com.barkinglabs.fi-apollo-ios
cookie: fi_acting_pet_id=redacted; fi_session_id=redacted; fi_partner=1; _fbp=redacted; _uetvid=redacted; ajs_anonymous_id=redacted; ajs_user_id=redacted; _ga=redacted; _hjSessionUser_1228176=redacted; _sp_id.54d7=redacted; _tt_enable_cookie=1; _ttp=redacted; ajs_group_id=null; tatari-session-cookie=redacted; _gcl_au=redacted
x-client: {"osVersion":"16.2","timezone":"America\/New_York","build":"RELEASE","deviceModel":"iPhone15,2","version":"3.0.5","platform":"IOS","deviceId":"redacted"}

{"operationName":"FiFeed","query":"query FiFeed($limit: Int, $pagingInstruction: PagingInstruction, $height: Int!, $width: Int!, $scale: Int) {\n  currentUser {\n    __typename\n    fiFeed(limit: $limit, pagingInstruction: $pagingInstruction) {\n      __typename\n      ...FiFeedDetails\n    }\n  }\n}\nfragment FiFeedDetails on FiFeed {\n  __typename\n  feedItems {\n    __typename\n    ...FiFeedItemDetails\n  }\n  pageInfo {\n    __typename\n    ...PageInfoDetails\n  }\n}\nfragment FiFeedItemDetails on FiFeedItem {\n  __typename\n  id\n  timestamp\n  ... on FiFeedActivityItem {\n    __typename\n    activity {\n      __typename\n      ...ActivityDetails\n      ... on Rest {\n        __typename\n        mapUrl(width: $width, height: $height, scale: $scale)\n      }\n      ... on Walk {\n        __typename\n        mapUrl(width: $width, height: $height, scale: $scale)\n      }\n      ... on Play {\n        __typename\n        mapUrl(width: $width, height: $height, scale: $scale)\n      }\n    }\n    pet {\n      __typename\n      ...BasePetInfo\n    }\n  }\n  ... on FiFeedGenericNotificationItem {\n    __typename\n    title\n    body {\n      __typename\n      ...FormatStringDetails\n    }\n    hideTimestamp\n  }\n  ... on FiFeedGoalStreakItem {\n    __typename\n    numDays\n    pet {\n      __typename\n      ...BasePetInfo\n    }\n  }\n  ... on FiFeedRankingUpdateItem {\n    __typename\n    pet {\n      __typename\n      ...BasePetInfo\n      breed {\n        __typename\n        ...BreedDetails\n      }\n    }\n    newBreedRank\n    newOverallRank\n    newOverallRankPercentile\n  }\n  ... on FiFeedPhotoAddedItem {\n    __typename\n    pet {\n      __typename\n      ...BasePetInfo\n    }\n    photo {\n      __typename\n      ...PhotoDetails\n    }\n    user {\n      __typename\n      firstName\n    }\n  }\n  ... on FiFeedLikableWalkItem {\n    __typename\n    followedPet {\n      __typename\n      ...BasePetInfo\n    }\n    walk {\n      __typename\n      ...StrangerWalkDetails\n    }\n  }\n}\nfragment ActivityDetails on Activity {\n  __typename\n  id\n  start\n  end\n  areaName\n  presentUser {\n    __typename\n    ...UserDetails\n  }\n  presentUserString\n  totalSteps\n  obfuscated\n  ... on Walk {\n    __typename\n    isLikable\n    likeCount\n    distance\n    mapPath {\n      __typename\n      ... on MapMatchedPath {\n        __typename\n        path {\n          __typename\n          ...PositionCoordinates\n        }\n      }\n      ... on UnmatchedPath {\n        __typename\n        locations {\n          __typename\n          ...LocationPoint\n        }\n      }\n    }\n  }\n  ... on Rest {\n    __typename\n    position {\n      __typename\n      ...PositionCoordinates\n    }\n    place {\n      __typename\n      ...PlaceDetails\n    }\n  }\n  ... on Play {\n    __typename\n    position {\n      __typename\n      ...PositionCoordinates\n    }\n    place {\n      __typename\n      ...PlaceDetails\n    }\n  }\n}\nfragment UserDetails on User {\n  __typename\n  id\n  email\n  firstName\n  lastName\n  phoneNumber\n  fiNewsNotificationsEnabled\n}\nfragment PositionCoordinates on Position {\n  __typename\n  latitude\n  longitude\n}\nfragment LocationPoint on Location {\n  __typename\n  date\n  errorRadius\n  position {\n    __typename\n    ...PositionCoordinates\n  }\n}\nfragment PlaceDetails on Place {\n  __typename\n  id\n  name\n  address\n  position {\n    __typename\n    ...PositionCoordinates\n  }\n  radius\n  type\n  coordinates {\n    __typename\n    ...PositionCoordinates\n  }\n}\nfragment BasePetInfo on BasePet {\n  __typename\n  id\n  name\n  homeCityState\n  photos {\n    __typename\n    first {\n      __typename\n      image {\n        __typename\n        ...ImageDetails\n      }\n    }\n  }\n  ... on StrangerPet {\n    __typename\n    followStatus\n  }\n  ... on Pet {\n    __typename\n    household {\n      __typename\n      id\n    }\n  }\n}\nfragment ImageDetails on Image {\n  __typename\n  id\n  fullSize\n}\nfragment FormatStringDetails on FormatString {\n  __typename\n  text\n  spans {\n    __typename\n    start\n    length\n    style\n  }\n}\nfragment BreedDetails on Breed {\n  __typename\n  id\n  name\n  popularityScore\n}\nfragment PhotoDetails on Photo {\n  __typename\n  id\n  date\n  image {\n    __typename\n    ...ImageDetails\n  }\n  likeCount\n  liked\n  caption\n  comments {\n    __typename\n    items {\n      __typename\n      ...CommentDetails\n    }\n    totalCount\n  }\n  pet {\n    __typename\n    ...BasePetInfo\n  }\n  poster {\n    __typename\n    ...PhotoPosterDetails\n  }\n}\nfragment CommentDetails on Comment {\n  __typename\n  id\n  comment\n  createdAt\n  strangerPet {\n    __typename\n    id\n    name\n    photos {\n      __typename\n      first {\n        __typename\n        image {\n          __typename\n          fullSize\n        }\n      }\n    }\n  }\n}\nfragment PhotoPosterDetails on PhotoPoster {\n  __typename\n  ... on StrangerUser {\n    __typename\n    ...StrangerUserDetails\n  }\n  ... on ExternalUser {\n    __typename\n    ...ExternalUserDetails\n  }\n}\nfragment StrangerUserDetails on StrangerUser {\n  __typename\n  id\n  firstName\n  profilePicture {\n    __typename\n    ...ImageDetails\n  }\n}\nfragment ExternalUserDetails on ExternalUser {\n  __typename\n  id\n  name\n  profilePicture {\n    __typename\n    ...ImageDetails\n  }\n}\nfragment StrangerWalkDetails on StrangerWalk {\n  __typename\n  id\n  distance\n  stepCount\n  liked\n  likeCount\n}\nfragment PageInfoDetails on PageInfo {\n  __typename\n  startCursor\n  endCursor\n  hasNextPage\n  hasPreviousPage\n}","variables":{"height":300,"limit":null,"pagingInstruction":{"cursor":"redacted","direction":"BACKWARD"},"scale":4,"width":373}}

And here is a sample response:

HTTP/2.0 200 
date: Wed, 14 Dec 2022 01:42:45 GMT
content-type: application/json; charset=utf-8
content-length: 6675
x-powered-by: Express
vary: Origin
access-control-allow-credentials: true

{"data":{"currentUser":{"__typename":"User","fiFeed":{"__typename":"FiFeed","feedItems":[{"__typename":"FiFeedActivityItem","id":"Activity:2022-08-21T02:54:37.237Z","timestamp":"2022-08-21T02:54:37.237Z","activity":{"__typename":"Walk","id":"redacted","start":"2022-08-21T02:49:18.425Z","end":"2022-08-21T02:59:56.050Z","areaName":"redacted","presentUser":{"__typename":"User","id":"redacted","email":"redacted","firstName":"redacted","lastName":"redacted","phoneNumber":"redacted","fiNewsNotificationsEnabled":false},"presentUserString":"You","totalSteps":1538,"obfuscated":false,"isLikable":false,"likeCount":0,"distance":256.31977063451683,"mapPath":{"__typename":"MapMatchedPath","path":[{"__typename":"Position","latitude":redacted,"longitude":redacted},{"__typename":"Position","latitude":redacted,"longitude":redacted},
"mapUrl":"https://barkinglabs-media.s3.amazonaws.com/maps/redacted.png"},"pet":{"__typename":"Pet","id":"redacted","name":"redacted","homeCityState":"redacted","photos":
cwille97 commented 1 year ago

I don't know GraphQL at the moment so in the mean time I wrote a quick and dirty script to scrape for activity data: https://github.com/cwille97/fiactivity

jmargel74 commented 1 year ago

I created a .net app (vb.net) with the help of sbabcock23's python code. If you want I can post the code that uses graphql that retrieves the information and the gathers it.

jmargel74 commented 1 year ago

I am an amateur programmer so please be kind when you see this code.. The first part gets the sessionID, the second part gets just some of the basic information from the account. I also have code that dives deeper into it and shows a daily activity of the steps taken. I also use gmap.net to show where the dog is on a map using the GPS coordinates. However what I can't seem to find is the rankings, etc..

Try
            Dim logincookie As CookieContainer

            Dim postData As String = "password=" & My.Settings.Password & "&email=" & My.Settings.UserName
            Dim tempCookies As New CookieContainer
            Dim encoding As New UTF8Encoding
            Dim byteData As Byte() = encoding.GetBytes(postData)

            Dim postReq As HttpWebRequest = DirectCast(System.Net.WebRequest.Create("https://api.tryfi.com/auth/login"), HttpWebRequest)

            petStatsTimer.Start()
                countdownTimer.Start()
                postReq.Method = "POST"
                postReq.KeepAlive = True
                postReq.CookieContainer = tempCookies
                postReq.CookieContainer = CookieJar
                postReq.AllowAutoRedirect = False
                postReq.ContentType = "application/x-www-form-urlencoded"
                postReq.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; ru; rv:1.9.2.3) Gecko/20100401 Firefox/4.0 (.NET CLR 3.5.30729)"
                postReq.ContentLength = byteData.Length

                Dim postreqstream As Stream = postReq.GetRequestStream()
                postreqstream.Write(byteData, 0, byteData.Length)
                postreqstream.Close()
                Dim postresponse As HttpWebResponse

                postresponse = DirectCast(postReq.GetResponse(), HttpWebResponse)
                Dim postresponseCode = postresponse.StatusCode

                tempCookies.Add(postresponse.Cookies)
                For Each tempCookie In postresponse.Cookies
                    CookieJar.Add(tempCookie)
                Next
                logincookie = tempCookies

                Dim postreqreader As New StreamReader(postresponse.GetResponseStream())

                Dim thepage As String = postreqreader.ReadToEnd
                dict = jss.Deserialize(Of Dictionary(Of String, Object))(thepage)

            For Each item In dict
                    session_id = dict("sessionId").ToString
                    user_id = dict("userId").ToString
                    email_id = dict("email").ToString
                Next
                fiSID = postresponse.Cookies.Item(0).Value 'fi SessionId

                Try

                    ' Get Pet Information

                    Dim restTryFiList As RestClient = New RestClient("https://api.tryfi.com/graphql?query=query " & QUERY_CURRENT_USER_FULL_DETAIL + FRAGMENT_USER_DETAILS + FRAGMENT_USER_FULL_DETAILS + FRAGMENT_PET_PROFILE + FRAGEMENT_BASE_PET_PROFILE + FRAGMENT_BASE_DETAILS + FRAGMENT_POSITION_COORDINATES + FRAGMENT_BREED_DETAILS + FRAGMENT_PHOTO_DETAILS + FRAGMENT_DEVICE_DETAILS + FRAGMENT_LED_DETAILS + FRAGMENT_OPERATIONAL_DETAILS + FRAGMENT_CONNECTION_STATE_DETAILS)
                    restTryFiList.AddDefaultHeader("Content-Type", "application/json")
                    Dim requestTryFiList As RestRequest = New RestRequest(Method.POST)

                    requestTryFiList.AddHeader("Content-Type", "application/json")
                    requestTryFiList.AddCookie("fi.sid", fiSID)
                    Dim responseTryFiList As RestResponse = restTryFiList.Execute(requestTryFiList)
                    TryFiDict = jss.Deserialize(Of Dictionary(Of String, Object))(responseTryFiList.Content)

                    For Each item In TryFiDict
                        first_name = TryFiDict.Item("data").Item("currentUser").Item("firstName")
                        last_name = TryFiDict.Item("data").Item("currentUser").Item("lastName")
                        pet_id = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).item("id")
                        pet_name = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).Item("name")
                        pet_birth_date = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0)("monthOfBirth") & "/" & TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0)("dayOfBirth") & "/" & TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0)("yearOfBirth")
                        pet_breed = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).item("breed").item("name")
                        pet_image = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).item("photos").item("first").item("image").item("fullSize")
                        pet_gender = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).item("gender")
                        location_last_updated = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("bases")(0).item("infoLastUpdated")
                        base_id = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("bases")(0).item("baseId")
                        network_name = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("bases")(0).item("networkName")
                        online = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("bases")(0).item("online")
                        onlineQuality = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("bases")(0).item("onlineQuality")
                        led_color = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).item("device").item("ledColor").item("name")
                        led_color_code = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).item("device").item("ledColor").item("ledColorCode")
                        module_id = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).item("device").item("moduleId")
                        battery_percentage = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).item("device").item("info").item("batteryPercent")
                        is_charging = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).item("device").item("info").item("isCharging")
                        led_enabled = TryFiDict.Item("data").item("currentUser").item("userHouseholds")(0).item("household").item("pets")(0).item("device").item("operationParams").item("ledEnabled")
                    Next

            Catch ex As Exception
                    MsgBox("Error encounered, please let the developer know of this error:" & ex.ToString)
                    Exit Sub
                End Try

        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try

Constants:

 Module modTryFiConst
    Public stats_monthly_date_start
    Public stats_monthly_date_end
    Public stats_monthly_total_steps
    Public stats_monthly_total_steps_goal

    Public stats_weekly_date_start
    Public stats_weekly_date_end
    Public stats_weekly_total_steps
    Public stats_weekly_total_steps_goal

    Public stats_daily_date_start
    Public stats_daily_date_end
    Public stats_daily_total_steps
    Public stats_daily_total_steps_goal

    Public TryFiDict As Dictionary(Of String, Object)
    Public fiStepsMonthlyDict As Dictionary(Of String, Object)
    Public dtMonthly = New DataTable("dtMonthly")
    Public dtDaily = New DataTable("dtDaily")

    Public Const API_HOST_URL_BASE = "https://api.tryfi.com"
    Public Const API_GRAPHQL = "/graphql"
    Public Const API_LOGIN = "/auth/login"

    Public Const PET_MODE_NORMAL = "NORMAL"
    Public Const PET_MODE_LOST = "LOST_DOG"
    Public Const PET_ACTIVITY_ONGOINGWALK = "OngoingWalk"
    Public Const PET_ACTIVITY_ONGOINGREST = "OngoingRest"
    Public Const PET_ACTIVITY_WALK = "Walk"
    Public Const PET_ACTIVITY_REST = "Rest"

    Public Const VAR_PET_ID = "__PET_ID__"

    Public Const QUERY_CURRENT_USER = "query {  currentUser {    ...UserDetails  }}"
    Public Const QUERY_CURRENT_USER_FULL_DETAIL = "query {  currentUser {    ...UserFullDetails  }}"
    Public Const QUERY_PET_CURRENT_LOCATION = "query {  pet (id: \" & VAR_PET_ID & "\) {    ongoingActivity(walksVersion: 1) {      __typename      ...OngoingActivityDetails    }  }}"

    Public QUERY_PET_ACTIVITY = "query {  pet (id: " & VAR_PET_ID & ") {       dailyStat: currentActivitySummary (period: DAILY) {      ...ActivitySummaryDetails    }    weeklyStat: currentActivitySummary (period: WEEKLY) {      ...ActivitySummaryDetails    }    monthlyStat: currentActivitySummary (period: MONTHLY) {      ...ActivitySummaryDetails    }  }}"

    Public Const QUERY_PET_DEVICE_DETAILS = "query {  pet (id: \" & VAR_PET_ID & "\"") {    __typename    ...PetProfile  }}"

    Public Const FRAGMENT_USER_DETAILS = "fragment UserDetails on User {  __typename   id  email  firstName  lastName  phoneNumber  fiNewsNotificationsEnabled  chipReseller {    __typename    id  }}"
    Public Const FRAGMENT_USER_FULL_DETAILS = "fragment UserFullDetails on User {  __typename  ...UserDetails  userHouseholds {    __typename    household {      __typename      pets {        __typename        ...PetProfile      }      bases {        __typename        ...BaseDetails      }    }  }}"
    Public Const FRAGEMENT_BASE_PET_PROFILE = "fragment BasePetProfile on BasePet {  __typename  id  name  homeCityState  yearOfBirth  monthOfBirth  dayOfBirth  gender  weight  isPurebred  breed {    __typename    ...BreedDetails  }  photos {    __typename    first {      __typename      ...PhotoDetails    }    items {      __typename      ...PhotoDetails    }  }  instagramHandle  }"
    Public Const FRAGMENT_BREED_DETAILS = "fragment BreedDetails on Breed {  __typename  id  name  popularityScore}"
    Public Const FRAGMENT_PHOTO_DETAILS = "fragment PhotoDetails on Photo {  __typename  id  caption  date  likeCount  liked  image {    __typename    fullSize  }}"
    Public Const FRAGMENT_PET_PROFILE = "fragment PetProfile on Pet {  __typename  ...BasePetProfile  chip {    __typename    shortId  }  device {    __typename    ...DeviceDetails  }}"
    Public Const FRAGMENT_DEVICE_DETAILS = "fragment DeviceDetails on Device {  __typename  id  moduleId  info  subscriptionId  hasActiveSubscription  hasSubscriptionOverride  nextLocationUpdateExpectedBy  operationParams {    __typename    ...OperationParamsDetails  }  lastConnectionState {    __typename    ...ConnectionStateDetails  }  ledColor {    __typename    ...LedColorDetails  }  availableLedColors {    __typename    ...LedColorDetails  }}"
    Public Const FRAGMENT_LED_DETAILS = "fragment LedColorDetails on LedColor {  __typename  ledColorCode  hexCode  name}"
    Public Const FRAGMENT_CONNECTION_STATE_DETAILS = "fragment ConnectionStateDetails on ConnectionState {  __typename  date  ... on ConnectedToUser {    user {      __typename      ...UserDetails    }  }  ... on ConnectedToBase {    chargingBase {      __typename      id    }  }  ... on ConnectedToCellular {    signalStrengthPercent  }  ... on UnknownConnectivity {    unknownConnectivity  }}"
    Public Const FRAGMENT_OPERATIONAL_DETAILS = "fragment OperationParamsDetails on OperationParams {  __typename  mode  ledEnabled  ledOffAt}"
    Public Const FRAGMENT_BASE_DETAILS = "fragment BaseDetails on ChargingBase {  __typename  baseId  name  position {    __typename    ...PositionCoordinates  }  infoLastUpdated  networkName  online  onlineQuality}"
    Public Const FRAGMENT_POSITION_COORDINATES = "fragment PositionCoordinates on Position {  __typename  latitude  longitude}"
    Public Const FRAGMENT_ONGOING_ACTIVITY_DETAILS = "fragment OngoingActivityDetails on OngoingActivity {  __typename  start  presentUser {    __typename    ...UserDetails  }  areaName  lastReportTimestamp  obfuscatedReason  totalSteps  uncertaintyInfo {    __typename    ...UncertaintyInfoDetails  }  ... on OngoingWalk {    distance    positions {      __typename      ...LocationPoint    }    path {      __typename      ...PositionCoordinates    }  }  ... on OngoingRest {    position {      __typename      ...PositionCoordinates    }    place {      __typename      ...PlaceDetails    }  }}"
    Public Const FRAGMENT_UNCERTAINTY_DETAILS = "fragment UncertaintyInfoDetails on UncertaintyInfo {  __typename  areaName  updatedAt  circle {    __typename    ...CircleDetails  }}"
    Public Const FRAGMENT_CIRCLE_DETAILS = "fragment CircleDetails on Circle {  __typename  radius  latitude  longitude}"
    Public Const FRAGMENT_LOCATION_POINT = "fragment LocationPoint on Location {  __typename  date  errorRadius  position {    __typename    ...PositionCoordinates  }}"
    Public Const FRAGMENT_PLACE_DETAILS = "fragment PlaceDetails on Place {  __typename  id  name  address  position {    __typename    ...PositionCoordinates  }  radius}"
    Public Const FRAGMENT_ACTIVITY_SUMMARY_DETAILS = "fragment ActivitySummaryDetails on ActivitySummary {  __typename  start  end  totalSteps  stepGoal  dailySteps {    __typename    date    totalSteps    stepGoal  }  totalDistance}"

    Public Const MUTATION_DEVICE_OPS = "mutation UpdateDeviceOperationParams($input: UpdateDeviceOperationParamsInput!) {  updateDeviceOperationParams(input: $input) {    __typename    ...DeviceDetails  }}"
    Public Const MUTATION_SET_LED_COLOR = "mutation SetDeviceLed($moduleId: String!, $ledColorCode: Int!) {  setDeviceLed(moduleId: $moduleId, ledColorCode: $ledColorCode) {    __typename    ...DeviceDetails  }}"
End Module
jmargel74 commented 1 year ago

This is the code I have for daily statistics. It takes the information and then plots it on a graph.

`    Dim restTryFiList As RestClient = New RestClient("https://api.tryfi.com/graphql?query=query query {  pet (id: """ & pet_id & """) {       dailyStat: currentActivitySummary (period: DAILY) {      ...ActivitySummaryDetails    }    weeklyStat: currentActivitySummary (period: WEEKLY) {      ...ActivitySummaryDetails    }    monthlyStat: currentActivitySummary (period: MONTHLY) {      ...ActivitySummaryDetails    }  }} " + FRAGMENT_ACTIVITY_SUMMARY_DETAILS)
        restTryFiList.AddDefaultHeader("Content-Type", "application/json")

        Dim requestTryFiList As RestRequest = New RestRequest(Method.POST)

        requestTryFiList.AddHeader("Content-Type", "application/json")
        requestTryFiList.AddCookie("fi.sid", fiSID)
        Dim responseTryFiList As RestResponse = restTryFiList.Execute(requestTryFiList)

        TryFiDict.Clear()

        TryFiDict = jss.Deserialize(Of Dictionary(Of String, Object))(responseTryFiList.Content)
        fiStepsMonthlyDict = jss.Deserialize(Of Dictionary(Of String, Object))(responseTryFiList.Content)

        For Each item In TryFiDict
            daily_pet_steps = TryFiDict.Item("data").item("pet").item("dailyStat").item("totalSteps")
            daily_pet_total_distance = TryFiDict.Item("data").item("pet").item("dailyStat").item("totalDistance")
            weekly_pet_steps = TryFiDict.Item("data").item("pet").item("weeklyStat").item("totalSteps")
            weekly_pet_total_distance = TryFiDict.Item("data").item("pet").item("weeklyStat").item("totalDistance")
            monthly_pet_steps = TryFiDict.Item("data").item("pet").item("monthlyStat").item("totalSteps")
            monthly_pet_total_distance = TryFiDict.Item("data").item("pet").item("monthlyStat").item("totalDistance")

            stats_monthly_date_start = TryFiDict.Item("data").item("pet").item("monthlyStat").item("start")
            stats_monthly_date_end = TryFiDict.Item("data").item("pet").item("monthlyStat").item("end")
            stats_monthly_total_steps = TryFiDict.Item("data").item("pet").item("monthlyStat").item("totalSteps")
            stats_monthly_total_steps_goal = TryFiDict.Item("data").item("pet").item("monthlyStat").item("stepGoal")
        Next

        'Monthly Statistics
        Dim keyColumn As DataColumn = New DataColumn("Key")
        keyColumn.DataType = System.Type.GetType("System.String")
        Dim valueColumn As DataColumn = New DataColumn("Value")
        valueColumn.DataType = System.Type.GetType("System.String")

        dtMonthly.Columns.Add(keyColumn)
        dtMonthly.Columns.Add(valueColumn)

        For Each item As KeyValuePair(Of String, Object) In fiStepsMonthlyDict.Item("data").item("pet").item("monthlyStat").item("dailySteps")
            For i = 0 To 31

                Dim iDate As String = fiStepsMonthlyDict.Item("data").item("pet").item("monthlyStat").item("dailySteps")(i).item("date")
                Dim oDate As DateTime = Convert.ToDateTime(iDate).ToShortDateString

                dtMonthly.rows.add(fiStepsMonthlyDict.Item("data").item("pet").item("monthlyStat").item("dailySteps")(i).item("date"), fiStepsMonthlyDict.Item("data").item("pet").item("monthlyStat").item("dailySteps")(i).item("totalSteps"))
            Next

        Next`
jwpalmer commented 9 months ago

Hi all, I'm super glad to come across this repo and some of the work being pulled together here. It's been really useful in trying to get my dog's data out of the Fi service.

I'm reasonably familiar with GraphQL and don't need a whole application just to grab this data once (I don't have a need to edit anything at this point)– I've just been using Postman and issuing GraphQL queries directly to api.tryfi.com.

I want a copy of my dog's history (walks, steps, sleep/naps, etc.), but find it hugely disappointing that I have to continue to pay for the service if I want access to these things. Seems pretty horrible that if I want to look back on a fun walk we had years from now, I have to keep the service active.

Since the API isn't documented, I haven't found an obvious way to get the Fi GraphQL schema, so I've been sifting through the code here and at @cwille97's repo.

My question: does anyone have the complete schema anywhere to basically replicate what you can get in the app?

So far I've gotten everything from the Fi app except the historical activity (queries like @jmargel74 's seem to just do the most recent days/month). Can anyone offer cursor or pagination schema to get activity by day all the way back to the first day of my dog's records? I've tried a bunch of things blind, but I'm just guessing here.

To be specific, if you go to the Activity or Sleep screens under the data tab, it looks like it's using a pet({ID}) object with:

{dailyStat/weeklyStat/monthlyStat}: currentActivitySummary (period: {DAILY/WEEKLY/MONTHLY})

For Activity, the above only loads the current period, but the app lets you swipe to go back to previous periods. I'm wondering how to emulate this in GraphQL? I've been able to use limit and cursor parameters in other places, like for the Feed or for Sleep to get more records, but the same approach doesn't work here (GraphQL returns an error that these parameters aren't recognized on currentActivitySummary).

Here's me getting more records for rest/sleep, for example, upping the limit parameter:

query {
    pet(id: "{ID}") {
        dailyStat: restSummaryFeed(cursor: null, period: DAILY, limit: 1500){
             # other return fragments here
        }
}

If the schema isn't known, I would really appreciate some suggestions to try for getting historical activity (steps, active time, strain). I'll also organize what I've figured out and will share it here.