279 lines
9.9 KiB
Go
279 lines
9.9 KiB
Go
package twitterscraper
|
|
|
|
import (
|
|
"errors"
|
|
"net/url"
|
|
"time"
|
|
)
|
|
|
|
func (s *Scraper) GetSpace(id string) (*Space, error) {
|
|
if !s.isLogged {
|
|
return nil, errors.New("scraper is not logged in")
|
|
}
|
|
|
|
req, err := s.newRequest("GET", "https://twitter.com/i/api/graphql/d03OdorPdZ_sH9V3D1_yWQ/AudioSpaceById")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
variables := map[string]interface{}{
|
|
"id": id,
|
|
"isMetatagsQuery": false,
|
|
"withReplays": true,
|
|
"withListeners": true,
|
|
}
|
|
|
|
features := map[string]interface{}{
|
|
"spaces_2022_h2_spaces_communities": true,
|
|
"spaces_2022_h2_clipping": true,
|
|
"creator_subscriptions_tweet_preview_api_enabled": true,
|
|
"rweb_tipjar_consumption_enabled": true,
|
|
"responsive_web_graphql_exclude_directive_enabled": true,
|
|
"verified_phone_label_enabled": false,
|
|
"communities_web_enable_tweet_community_results_fetch": true,
|
|
"c9s_tweet_anatomy_moderator_badge_enabled": true,
|
|
"articles_preview_enabled": true,
|
|
"responsive_web_graphql_skip_user_profile_image_extensions_enabled": false,
|
|
"tweetypie_unmention_optimization_enabled": true,
|
|
"responsive_web_edit_tweet_api_enabled": true,
|
|
"graphql_is_translatable_rweb_tweet_is_translatable_enabled": true,
|
|
"view_counts_everywhere_api_enabled": true,
|
|
"longform_notetweets_consumption_enabled": true,
|
|
"responsive_web_twitter_article_tweet_consumption_enabled": true,
|
|
"tweet_awards_web_tipping_enabled": false,
|
|
"creator_subscriptions_quote_tweet_preview_enabled": false,
|
|
"freedom_of_speech_not_reach_fetch_enabled": true,
|
|
"standardized_nudges_misinfo": true,
|
|
"tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": true,
|
|
"rweb_video_timestamps_enabled": true,
|
|
"longform_notetweets_rich_text_read_enabled": true,
|
|
"longform_notetweets_inline_media_enabled": true,
|
|
"responsive_web_graphql_timeline_navigation_enabled": true,
|
|
"responsive_web_enhance_cards_enabled": false,
|
|
}
|
|
|
|
query := url.Values{}
|
|
query.Set("variables", mapToJSONString(variables))
|
|
query.Set("features", mapToJSONString(features))
|
|
req.URL.RawQuery = query.Encode()
|
|
|
|
var spaceData space
|
|
err = s.RequestAPI(req, &spaceData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
space := spaceData.parse()
|
|
|
|
if space.ID == "" {
|
|
return nil, errors.New("some erorr happend")
|
|
}
|
|
|
|
return space, nil
|
|
}
|
|
|
|
type Topic struct {
|
|
ID string
|
|
Title string
|
|
}
|
|
|
|
type SpaceUser struct {
|
|
UserID string
|
|
Username string
|
|
Name string
|
|
Avatar string
|
|
IsVerified bool
|
|
ConnectedAt time.Time
|
|
}
|
|
|
|
type SpaceParticipants struct {
|
|
TotalCount int
|
|
CurrentCount int
|
|
Admins []*SpaceUser
|
|
Speakers []*SpaceUser
|
|
Listeners []*SpaceUser
|
|
}
|
|
|
|
type Space struct {
|
|
ID string
|
|
State string
|
|
Title string
|
|
ContentType string
|
|
Topics []Topic
|
|
Participants SpaceParticipants
|
|
CreatedAt time.Time
|
|
ScheduledStart time.Time
|
|
StartedAt time.Time
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
type spaceUser struct {
|
|
PeriscopeUserID string `json:"periscope_user_id"`
|
|
Start int64 `json:"start"`
|
|
TwitterScreenName string `json:"twitter_screen_name"`
|
|
DisplayName string `json:"display_name"`
|
|
AvatarURL string `json:"avatar_url"`
|
|
IsVerified bool `json:"is_verified"`
|
|
IsMutedByAdmin bool `json:"is_muted_by_admin"`
|
|
IsMutedByGuest bool `json:"is_muted_by_guest"`
|
|
UserResults struct {
|
|
RestID string `json:"rest_id"`
|
|
Result struct {
|
|
Typename string `json:"__typename"`
|
|
IdentityProfileLabelsHighlightedLabel struct {
|
|
} `json:"identity_profile_labels_highlighted_label"`
|
|
IsBlueVerified bool `json:"is_blue_verified"`
|
|
Legacy struct {
|
|
} `json:"legacy"`
|
|
} `json:"result"`
|
|
} `json:"user_results"`
|
|
}
|
|
|
|
type space struct {
|
|
Data struct {
|
|
AudioSpace struct {
|
|
Metadata struct {
|
|
RestID string `json:"rest_id"`
|
|
State string `json:"state"`
|
|
Title string `json:"title"`
|
|
MediaKey string `json:"media_key"`
|
|
CreatedAt int64 `json:"created_at"`
|
|
ScheduledStart int64 `json:"scheduled_start"`
|
|
StartedAt int64 `json:"started_at"`
|
|
UpdatedAt int64 `json:"updated_at"`
|
|
ContentType string `json:"content_type"`
|
|
CreatorResults struct {
|
|
Result struct {
|
|
Typename string `json:"__typename"`
|
|
ID string `json:"id"`
|
|
RestID string `json:"rest_id"`
|
|
HasGraduatedAccess bool `json:"has_graduated_access"`
|
|
IsBlueVerified bool `json:"is_blue_verified"`
|
|
ProfileImageShape string `json:"profile_image_shape"`
|
|
Legacy legacyUser `json:"legacy"`
|
|
} `json:"result"`
|
|
} `json:"creator_results"`
|
|
ConversationControls int `json:"conversation_controls"`
|
|
DisallowJoin bool `json:"disallow_join"`
|
|
IsEmployeeOnly bool `json:"is_employee_only"`
|
|
IsLocked bool `json:"is_locked"`
|
|
IsMuted bool `json:"is_muted"`
|
|
IsSpaceAvailableForClipping bool `json:"is_space_available_for_clipping"`
|
|
IsSpaceAvailableForReplay bool `json:"is_space_available_for_replay"`
|
|
MentionedUsers []struct {
|
|
RestID string `json:"rest_id"`
|
|
} `json:"mentioned_users"`
|
|
NarrowCastSpaceType int `json:"narrow_cast_space_type"`
|
|
NoIncognito bool `json:"no_incognito"`
|
|
TotalReplayWatched int `json:"total_replay_watched"`
|
|
TotalLiveListeners int `json:"total_live_listeners"`
|
|
Topics []struct {
|
|
Topic struct {
|
|
TopicID string `json:"topic_id"`
|
|
Name string `json:"name"`
|
|
} `json:"topic"`
|
|
} `json:"topics"`
|
|
TweetResults struct {
|
|
Result tweet `json:"result"`
|
|
} `json:"tweet_results"`
|
|
MaxGuestSessions int `json:"max_guest_sessions"`
|
|
MaxAdminCapacity int `json:"max_admin_capacity"`
|
|
} `json:"metadata"`
|
|
|
|
IsSubscribed bool `json:"is_subscribed"`
|
|
Participants struct {
|
|
Total int `json:"total"`
|
|
Admins []spaceUser `json:"admins"`
|
|
Speakers []spaceUser `json:"speakers"`
|
|
Listeners []spaceUser `json:"listeners"`
|
|
} `json:"participants"`
|
|
Sharings struct {
|
|
Items []struct {
|
|
SharingID string `json:"sharing_id"`
|
|
CreatedAtMs int64 `json:"created_at_ms"`
|
|
UpdatedAtMs int64 `json:"updated_at_ms"`
|
|
SharedItem struct {
|
|
Typename string `json:"__typename"`
|
|
TweetResults struct {
|
|
Result struct {
|
|
Typename string `json:"__typename"`
|
|
RestID string `json:"rest_id"`
|
|
Core tweet `json:"result"`
|
|
} `json:"tweet_results"`
|
|
} `json:"shared_item"`
|
|
UserResults struct {
|
|
Result struct {
|
|
Typename string `json:"__typename"`
|
|
ID string `json:"id"`
|
|
RestID string `json:"rest_id"`
|
|
AffiliatesHighlightedLabel struct {
|
|
} `json:"affiliates_highlighted_label"`
|
|
HasGraduatedAccess bool `json:"has_graduated_access"`
|
|
IsBlueVerified bool `json:"is_blue_verified"`
|
|
ProfileImageShape string `json:"profile_image_shape"`
|
|
Legacy legacyUser `json:"legacy"`
|
|
TipjarSettings struct {
|
|
IsEnabled bool `json:"is_enabled"`
|
|
CashAppHandle string `json:"cash_app_handle"`
|
|
} `json:"tipjar_settings"`
|
|
} `json:"result"`
|
|
} `json:"user_results"`
|
|
} `json:"shared_item"`
|
|
} `json:"items"`
|
|
} `json:"sharings"`
|
|
} `json:"audioSpace"`
|
|
} `json:"data"`
|
|
}
|
|
|
|
func (user *spaceUser) parse() *SpaceUser {
|
|
result := &SpaceUser{
|
|
UserID: user.UserResults.RestID,
|
|
Username: user.TwitterScreenName,
|
|
Name: user.DisplayName,
|
|
Avatar: user.AvatarURL,
|
|
IsVerified: user.IsVerified,
|
|
ConnectedAt: time.UnixMilli(user.Start),
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (space *space) parse() *Space {
|
|
result := &Space{
|
|
ID: space.Data.AudioSpace.Metadata.RestID,
|
|
State: space.Data.AudioSpace.Metadata.State,
|
|
Title: space.Data.AudioSpace.Metadata.Title,
|
|
ContentType: space.Data.AudioSpace.Metadata.ContentType,
|
|
Topics: []Topic{},
|
|
Participants: SpaceParticipants{
|
|
TotalCount: space.Data.AudioSpace.Metadata.TotalLiveListeners,
|
|
CurrentCount: space.Data.AudioSpace.Participants.Total,
|
|
},
|
|
CreatedAt: time.UnixMilli(space.Data.AudioSpace.Metadata.CreatedAt),
|
|
ScheduledStart: time.UnixMilli(space.Data.AudioSpace.Metadata.ScheduledStart),
|
|
StartedAt: time.UnixMilli(space.Data.AudioSpace.Metadata.StartedAt),
|
|
UpdatedAt: time.UnixMilli(space.Data.AudioSpace.Metadata.UpdatedAt),
|
|
}
|
|
|
|
for _, topic := range space.Data.AudioSpace.Metadata.Topics {
|
|
result.Topics = append(result.Topics, Topic{
|
|
ID: topic.Topic.TopicID,
|
|
Title: topic.Topic.Name,
|
|
})
|
|
}
|
|
|
|
for _, admin := range space.Data.AudioSpace.Participants.Admins {
|
|
result.Participants.Admins = append(result.Participants.Admins, admin.parse())
|
|
}
|
|
|
|
for _, speaker := range space.Data.AudioSpace.Participants.Speakers {
|
|
result.Participants.Speakers = append(result.Participants.Speakers, speaker.parse())
|
|
}
|
|
|
|
for _, listener := range space.Data.AudioSpace.Participants.Listeners {
|
|
result.Participants.Listeners = append(result.Participants.Listeners, listener.parse())
|
|
|
|
}
|
|
|
|
return result
|
|
}
|