Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import json
- from typing import Dict, List
- import html
- from datetime import datetime
- import subprocess
- import os
- """
- instructions to add a category
- 1. add the table header
- 2. add the table defenition and change the data class to what you want
- 3. add the json cateogry and make it the exact same as the data class. dont forget the trailing comma
- """
- def parse_twitter_user(entry: Dict) -> Dict:
- """Extract relevant user information from a Twitter API entry."""
- try:
- user_data = entry.get("content", {}).get("itemContent", {}).get("user_results", {}).get("result", {})
- if not user_data or not user_data.get("legacy"):
- print(f"Warning: Invalid user data structure: {entry}")
- return None
- legacy = user_data["legacy"]
- return {
- "profile_image": legacy.get("profile_image_url_https", ""),
- "screen_name": legacy.get("screen_name", ""),
- "name": html.escape(legacy.get("name", "")),
- "description": html.escape(legacy.get("description", "")),
- "following": legacy.get("friends_count", 0),
- "followers": legacy.get("followers_count", 0),
- "location": html.escape(legacy.get("location", "")),
- "follows_you": legacy.get("followed_by", False)
- }
- except Exception as e:
- print(f"Error parsing user data: {e}")
- return None
- def generate_html(users: List[Dict]) -> str:
- """Generate HTML page with Twitter following table."""
- users = [user for user in users if user is not None]
- if not users:
- return "<h1>No valid user data found</h1>"
- html_head = """
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Twitter Following Management</title>
- <style>
- body {
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
- margin: 0;
- background: #f7f9fa;
- display: flex;
- justify-content: center;
- min-height: 100vh;
- }
- .container {
- width: 95%;
- padding: 20px;
- }
- table {
- width: 100%;
- border-collapse: collapse;
- background: white;
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
- border-radius: 8px;
- overflow: hidden;
- }
- th, td {
- padding: 12px;
- text-align: left;
- border-bottom: 1px solid #eee;
- position: relative;
- }
- th {
- background: #1DA1F2;
- color: white;
- font-weight: 500;
- }
- .clickable {
- cursor: pointer;
- padding: 2px !important;
- }
- /* Remove the previous hover effect */
- .clickable:hover {
- background-color: transparent;
- }
- /* Add column highlighting */
- td[class*="checkbox-column"],
- th[class*="checkbox-column"] {
- position: relative;
- }
- /* Create the column highlight effect */
- td[class*="checkbox-column"]:hover::after,
- th[class*="checkbox-column"]:hover::after,
- td[class*="checkbox-column"]:hover ~ td[class*="checkbox-column"]::after,
- th[class*="checkbox-column"]:hover ~ td[class*="checkbox-column"]::after {
- content: '';
- position: absolute;
- background-color: #f8f8f8;
- width: 100%;
- height: 100000px; /* Very large height to ensure coverage */
- left: 0;
- top: -50000px; /* Negative offset to center the highlight */
- z-index: -1;
- pointer-events: none;
- }
- th:first-child, td:first-child {
- position: sticky;
- left: 0;
- background-color: #f8f8f8;
- z-index: 2;
- background: #1DA1F2;
- font-weight: 500;
- box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.4);
- }
- thead {
- position: sticky;
- top: 0;
- background-color: white;
- z-index: 1000;
- box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.4);
- }
- .profile-img {
- width: 48px;
- height: 48px;
- border-radius: 50%;
- vertical-align: middle;
- }
- .user-info {
- display: flex;
- align-items: center;
- gap: 12px;
- }
- .names {
- display: flex;
- flex-direction: column;
- }
- .display-name {
- font-weight: 600;
- }
- .username {
- color: #536471;
- text-decoration: none;
- }
- .username:hover {
- text-decoration: underline;
- }
- .description {
- color: #536471;
- max-width: 300px;
- }
- .stats {
- color: #536471;
- }
- .location {
- color: #536471;
- }
- .checkbox-wrapper {
- display: flex;
- justify-content: center;
- }
- input[type="checkbox"] {
- width: 20px;
- height: 20px;
- cursor: pointer;
- }
- tr:hover {
- background: #f7f9fa;
- }
- .follows-you {
- color: #1DA1F2;
- text-align: center;
- font-size: 18px;
- }
- .checkbox-column {
- padding: 2px;
- }
- </style>
- </head>
- """
- html_body = """
- <body>
- <div class="container">
- <h1>Twitter Following Management</h1>
- <p>Total Following: {total_users}</p>
- <table id="following-table">
- <thead>
- <tr>
- <th>Count</th>
- <th>Profile</th>
- <th>Description</th>
- <th>Stats</th>
- <th>Follows You</th>
- <th>Location</th>
- <th class="checkbox-column">Unfollow</th>
- <th class="checkbox-column">AI Companies</th>
- <th class="checkbox-column">AI People</th>
- <th class="checkbox-column">Anon Coders</th>
- <th class="checkbox-column">Named Coders</th>
- <th class="checkbox-column">Investors</th>
- <th class="checkbox-column">Meme Pages</th>
- <th class="checkbox-column">Founders</th>
- <th class="checkbox-column">Design</th>
- <th class="checkbox-column">Companies</th>
- </tr>
- </thead>
- <tbody>
- {table_rows}"""
- html_tail = """
- <script>
- // Function to handle checkbox state and storage
- function handleCheckboxChange(checkbox) {
- const screenName = checkbox.getAttribute('data-screen-name');
- const category = checkbox.getAttribute('data-type');
- const storageKey = `${screenName}_${category}`;
- localStorage.setItem(storageKey, checkbox.checked);
- }
- // Initialize all checkboxes
- document.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
- const screenName = checkbox.getAttribute('data-screen-name');
- const category = checkbox.getAttribute('data-type');
- const storageKey = `${screenName}_${category}`;
- // Load saved state
- checkbox.checked = localStorage.getItem(storageKey) === 'true';
- // Add change listener
- checkbox.addEventListener('change', () => handleCheckboxChange(checkbox));
- });
- // Handle clicks on the entire cell
- document.querySelectorAll('.clickable').forEach(td => {
- td.addEventListener('click', (event) => {
- // Only toggle if the click wasn't directly on the checkbox
- if (event.target.type !== 'checkbox') {
- const checkbox = td.querySelector('input[type="checkbox"]');
- checkbox.checked = !checkbox.checked;
- handleCheckboxChange(checkbox);
- }
- });
- });
- // Add export button
- const exportButton = document.createElement('button');
- exportButton.textContent = 'Export Categories';
- exportButton.style.cssText = 'position: fixed; bottom: 20px; right: 20px; padding: 10px 20px; background: #1DA1F2; color: white; border: none; border-radius: 4px; cursor: pointer;';
- document.body.appendChild(exportButton);
- exportButton.addEventListener('click', () => {
- const categories = {
- 'unfollow': [],
- 'ai companies': [],
- 'ai people': [],
- 'anon coders': [],
- 'named coders': [],
- 'investors': [],
- 'meme pages': [],
- 'founders': [],
- 'design': [],
- 'companies': []
- };
- document.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
- const screenName = checkbox.getAttribute('data-screen-name');
- const category = checkbox.getAttribute('data-type');
- if (checkbox.checked) {
- categories[category].push(screenName);
- }
- });
- const jsonString = JSON.stringify(categories, null, 2);
- const blob = new Blob([jsonString], { type: 'application/json' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = 'twitter_categories.json';
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- console.log(jsonString);
- });
- </script>
- </tbody>
- </table>
- </div>
- </body>
- </html>
- """
- row_template = """
- <tr>
- <td style="background-color: lightgray">
- <div>{count}</div>
- </td>
- <td>
- <div class="user-info">
- <img src="{profile_image}" alt="{screen_name}" class="profile-img">
- <div class="names">
- <span class="display-name">{name}</span>
- <a href="https://twitter.com/{screen_name}" target="_blank" class="username">@{screen_name}</a>
- </div>
- </div>
- </td>
- <td class="description">{description}</td>
- <td class="stats">
- <div>Following: {following}</div>
- <div>Followers: {followers}</div>
- </td>
- <td class="stats"
- <div>{follows_you}</div>
- </td>
- <td class="location">
- {location}
- </td>
- <td class="clickable checkbox-column">
- <input type="checkbox" data-screen-name="{screen_name}" data-type="unfollow">
- </td>
- <td class="clickable checkbox-column">
- <input type="checkbox" data-screen-name="{screen_name}" data-type="ai companies">
- </td>
- <td class="clickable checkbox-column">
- <input type="checkbox" data-screen-name="{screen_name}" data-type="ai people">
- </td>
- <td class="clickable checkbox-column">
- <input type="checkbox" data-screen-name="{screen_name}" data-type="anon coders">
- </td>
- <td class="clickable checkbox-column">
- <input type="checkbox" data-screen-name="{screen_name}" data-type="named coders">
- </td>
- <td class="clickable checkbox-column">
- <input type="checkbox" data-screen-name="{screen_name}" data-type="investors">
- </td>
- <td class="clickable checkbox-column">
- <input type="checkbox" data-screen-name="{screen_name}" data-type="meme pages">
- </td>
- <td class="clickable checkbox-column">
- <input type="checkbox" data-screen-name="{screen_name}" data-type="founders">
- </td>
- <td class="clickable checkbox-column">
- <input type="checkbox" data-screen-name="{screen_name}" data-type="design">
- <td class="clickable checkbox-column">
- <input type="checkbox" data-screen-name="{screen_name}" data-type="companies">
- </td>
- </tr>"""
- # Generate table rows
- table_rows = []
- for c, user in enumerate(users):
- c += 1
- follows_you_text = "✓" if user["follows_you"] else ""
- location_text = user["location"] if user["location"] else ""
- row_html = row_template.format(
- count = str(c),
- profile_image=user["profile_image"],
- screen_name=user["screen_name"],
- name=user["name"],
- description=user["description"],
- following=user["following"],
- followers=user["followers"],
- follows_you=follows_you_text,
- location=location_text
- )
- table_rows.append(row_html)
- # Generate final HTML
- return html_head + html_body.format(
- table_rows="\n".join(table_rows),
- total_users=len(users)
- ) + html_tail
- def main():
- file_name = input('what is the name of the file? ')
- users = []
- # Read and parse input JSON
- with open(file_name, 'r') as f:
- for c, line in enumerate(f):
- print(c)
- line = json.loads(line)
- try:
- entries = line["data"]["user"]["result"]["timeline"]["timeline"]["instructions"][0]["entries"]
- except (IndexError, KeyError, json.JSONDecodeError) as e:
- entries = line["data"]["user"]["result"]["timeline"]["timeline"]["instructions"][2]["entries"]
- print(type(entries))
- users.extend([parse_twitter_user(entry) for entry in entries[:-2]]) # Excluding last two entries
- # Generate HTML
- html_output = generate_html(users)
- # Write to file
- file_name = os.path.join('twitter_following_html', f'twitter_following_{datetime.now().isoformat()[:-7]}.html')
- with open(file_name, 'w', encoding='utf-8') as f:
- f.write(html_output)
- subprocess.run(['firefox', file_name])
- if __name__ == "__main__":
- main()
Advertisement
Add Comment
Please, Sign In to add comment