Task: gitlab.follow-user
Goal: Follow convexegg, yjlou on Gitlab
Task configuration:
account_list: convexegg, yjlou

Validation code:
import unicodedata
from typing import Any, Dict, List

import requests
import json
import os


def _normalize(s):
    """Lowercase and strip diacritics for flexible matching."""
    s = s.lower().strip()
    # Decompose unicode characters, then remove combining marks (diacritics)
    return "".join(
        c for c in unicodedata.normalize("NFD", s)
        if unicodedata.category(c) != "Mn"
    )

def _get_headers():
    return {
        "Content-Type": "application/json",
        "Accept": "application/json",
        **json.loads(os.getenv("GITLAB_EXTRA_HTTP_HEADERS", "{}")),
    }

def validate(messages: List[Dict[str, str]], config: Dict[str, Any]) -> int:

    if not messages:
        raise ValueError("Messages are empty")

    # Check whether we have the necessary information to validate
    if "account_list" not in config:
        raise ValueError("Target must contain 'account_list'")

    # Get the current user if username is not provided
    if "username" in config:
        username = config["username"]
    else:
        user_url = os.getenv("GITLAB_BASE_URL", "").rstrip("/") + "/api/v4/user"
        response = requests.get(url=user_url, headers=_get_headers())
        if response.status_code != 200:
            return 0
        username = response.json()["username"]

    # Get the user ID for the username
    username_url = os.getenv("GITLAB_BASE_URL", "").rstrip("/") + "/api/v4/users?username=" + username
    response = requests.get(url=username_url, headers=_get_headers())

    if response.status_code != 200 or not response.json():
        # User not found
        return 0

    user_id = response.json()[0]["id"]

    # Get all users that this user follows
    following_url = os.getenv("GITLAB_BASE_URL", "").rstrip("/") + "/api/v4/users/" + str(user_id) + "/following"

    # Handle pagination to get all followed users
    all_followed_users = []
    page = 1
    per_page = 100

    while True:
        response = requests.get(
            url=following_url,
            headers=_get_headers(),
            params={"page": page, "per_page": per_page},
        )

        if response.status_code != 200:
            return 0  # Error retrieving followed users

        followed_users = response.json()
        all_followed_users.extend(followed_users)

        # Check if we've reached the end of pagination
        if len(followed_users) < per_page:
            break

        page += 1

    # Extract usernames and names of followed users (normalized)
    followed_usernames = [_normalize(user["username"]) for user in all_followed_users]
    followed_names = [_normalize(user["name"]) for user in all_followed_users]

    # Parse account_list - it could be a list or a comma-separated string
    if isinstance(config["account_list"], list):
        accounts = config["account_list"]
    else:
        accounts = [a.strip() for a in config["account_list"].split(",")]

    # Check if every account in account_list is in the followed users
    # Match by username or by name (substring match for flexibility)
    for account in accounts:
        account_norm = _normalize(account)
        found = (
            account_norm in followed_usernames
            or any(account_norm in name for name in followed_names)
        )
        if not found:
            return 0

    return 1