APE Price: $1.91 (+14.81%)

Contract Diff Checker

Contract Name:
RSS2PodcastEngagementV2

Contract Source Code:

File 1 of 1 : RSS2PodcastEngagementV2

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

interface RSS2PodcastEngagementToken {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

contract RSS2PodcastEngagementV2 {
    enum EngagementType {
        FEEDBACK,
        RATING,
        SHARE_TWITTER,
        SHARE_LINKEDIN,
        SUGGESTION,
        FIRST_COMMENT,
        QUALITY_FEEDBACK,
        WEEKLY_ACTIVE,
        STREAK_BONUS,
        COMPLETION,
        REFERRAL,
        COMMUNITY_CHOICE,
        DISCUSSION_STARTER
    }

    enum ModerationType {
        HATEFUL_CONTENT,
        SPAM,
        INAPPROPRIATE,
        MISLEADING,
        OTHER
    }

    struct Episode {
        uint256 feedbackCount;
        uint256 totalRating;
        address firstCommenter;
        mapping(uint256 => uint256) feedbackVotes;
    }

    struct Feedback {
        address user;
        string content;
        bool isHidden;
        uint256 timestamp;
        uint256 replyCount;
        bool isDiscussionStarter;
        ModerationType moderationType;
        uint256 reportCount;
    }

    address public owner;
    RSS2PodcastEngagementToken public dixoToken;
    mapping(address => bool) public moderators;
    mapping(address => bool) public bannedUsers;
    mapping(EngagementType => uint256) public rewardAmounts;

    mapping(uint256 => Episode) public episodes;
    mapping(uint256 => Feedback[]) public episodeFeedback;
    mapping(uint256 => mapping(address => mapping(EngagementType => bool))) public userEngagements;
    mapping(uint256 => mapping(address => string)) public shareProofs;

    mapping(uint256 => mapping(uint256 => mapping(address => bool))) public hasVotedOnFeedback;
    mapping(address => uint256) public communityPoints;
    mapping(address => uint256) public userLastEngagement;
    mapping(address => uint256) public weeklyEngagementCount;
    mapping(address => uint256) public streakCount;
    mapping(address => address) public referredBy;

    mapping(address => uint256) public userReportCount;
    mapping(uint256 => mapping(uint256 => mapping(address => bool))) public hasReportedFeedback;
    uint256 public constant REPORT_THRESHOLD = 5;

    event ModeratorAdded(address moderator);
    event ModeratorRemoved(address moderator);
    event UserBanned(address user, string reason);
    event UserUnbanned(address user);
    event FeedbackSubmitted(uint256 indexed episodeId, address user, uint256 feedbackIndex);
    event FeedbackModerated(uint256 indexed episodeId, uint256 feedbackIndex, ModerationType moderationType);
    event FeedbackReported(uint256 indexed episodeId, uint256 feedbackIndex, address reporter);
    event RewardEarned(address user, uint256 indexed episodeId, EngagementType engagementType, uint256 amount);
    event RewardAmountUpdated(EngagementType engagementType, uint256 oldAmount, uint256 newAmount);
    event ShareSubmitted(address user, uint256 indexed episodeId, string platform, string proofHash);
    event RatingSubmitted(uint256 indexed episodeId, address user, uint8 rating);

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    modifier onlyModerator() {
        require(moderators[msg.sender], "Not moderator");
        _;
    }

    modifier notBanned() {
        require(!bannedUsers[msg.sender], "User is banned");
        _;
    }

    constructor(address _dixoToken) {
        owner = msg.sender;
        moderators[msg.sender] = true;
        dixoToken = RSS2PodcastEngagementToken(_dixoToken);
    }

    function addModerator(address moderator) external onlyOwner {
        require(!moderators[moderator], "Already moderator");
        moderators[moderator] = true;
        emit ModeratorAdded(moderator);
    }

    function removeModerator(address moderator) external onlyOwner {
        require(moderator != owner, "Cannot remove owner");
        require(moderators[moderator], "Not moderator");
        moderators[moderator] = false;
        emit ModeratorRemoved(moderator);
    }

    function banUser(address user, string calldata reason) external onlyModerator {
        require(!bannedUsers[user], "Already banned");
        bannedUsers[user] = true;
        emit UserBanned(user, reason);
    }

    function unbanUser(address user) external onlyModerator {
        require(bannedUsers[user], "Not banned");
        bannedUsers[user] = false;
        emit UserUnbanned(user);
    }

    function submitFeedback(
        uint256 episodeId,
        string calldata feedback
    ) external notBanned {
        require(episodeId > 0, "Episode ID invalid");
        require(!userEngagements[episodeId][msg.sender][EngagementType.FEEDBACK], "Already gave feedback");
        require(bytes(feedback).length >= 50, "Feedback too short");

        uint256 feedbackIndex = episodeFeedback[episodeId].length;
        episodeFeedback[episodeId].push(Feedback({
            user: msg.sender,
            content: feedback,
            isHidden: false,
            timestamp: block.timestamp,
            replyCount: 0,
            isDiscussionStarter: false,
            moderationType: ModerationType.OTHER,
            reportCount: 0
        }));

        userEngagements[episodeId][msg.sender][EngagementType.FEEDBACK] = true;
        episodes[episodeId].feedbackCount++;

        if (episodes[episodeId].feedbackCount == 1) {
            episodes[episodeId].firstCommenter = msg.sender;
            _distributeReward(msg.sender, EngagementType.FIRST_COMMENT);
        }

        _distributeReward(msg.sender, EngagementType.FEEDBACK);
        _updateEngagementMetrics(msg.sender);

        emit FeedbackSubmitted(episodeId, msg.sender, feedbackIndex);
    }

    function submitRating(uint256 episodeId, uint8 rating) external notBanned {
        require(episodeId > 0, "Episode ID invalid");
        require(!userEngagements[episodeId][msg.sender][EngagementType.RATING], "Already rated");
        require(rating >= 1 && rating <= 5, "Invalid rating");

        userEngagements[episodeId][msg.sender][EngagementType.RATING] = true;
        episodes[episodeId].totalRating += rating;

        _distributeReward(msg.sender, EngagementType.RATING);
        _updateEngagementMetrics(msg.sender);

        emit RatingSubmitted(episodeId, msg.sender, rating);
    }

    function moderateFeedback(
        uint256 episodeId,
        uint256 feedbackIndex,
        ModerationType moderationType
    ) external onlyModerator {
        require(feedbackIndex < episodeFeedback[episodeId].length, "Invalid feedback index");
        Feedback storage feedback = episodeFeedback[episodeId][feedbackIndex];
        feedback.isHidden = true;
        feedback.moderationType = moderationType;
        emit FeedbackModerated(episodeId, feedbackIndex, moderationType);
    }

    function reportFeedback(
        uint256 episodeId,
        uint256 feedbackIndex
    ) external notBanned {
        require(!hasReportedFeedback[episodeId][feedbackIndex][msg.sender], "Already reported");
        require(episodeFeedback[episodeId][feedbackIndex].user != msg.sender, "Cannot report own feedback");

        Feedback storage feedback = episodeFeedback[episodeId][feedbackIndex];
        feedback.reportCount++;
        hasReportedFeedback[episodeId][feedbackIndex][msg.sender] = true;

        if (feedback.reportCount >= REPORT_THRESHOLD) {
            feedback.isHidden = true;
        }

        emit FeedbackReported(episodeId, feedbackIndex, msg.sender);
    }

    function setRewardAmount(EngagementType engagementType, uint256 amount) external onlyOwner {
        uint256 oldAmount = rewardAmounts[engagementType];
        rewardAmounts[engagementType] = amount;
        emit RewardAmountUpdated(engagementType, oldAmount, amount);
    }

    function setBatchRewardAmounts(
        EngagementType[] calldata engagementTypes,
        uint256[] calldata amounts
    ) external onlyOwner {
        require(engagementTypes.length == amounts.length, "Length mismatch");
        for(uint i = 0; i < engagementTypes.length; i++) {
            uint256 oldAmount = rewardAmounts[engagementTypes[i]];
            rewardAmounts[engagementTypes[i]] = amounts[i];
            emit RewardAmountUpdated(engagementTypes[i], oldAmount, amounts[i]);
        }
    }

    function _distributeReward(
        address user,
        EngagementType engagementType
    ) internal {
        uint256 rewardAmount = rewardAmounts[engagementType];
        if (rewardAmount > 0) {
            require(dixoToken.transfer(user, rewardAmount), "Reward transfer failed");
            emit RewardEarned(user, 0, engagementType, rewardAmount);
        }
    }

    function _updateEngagementMetrics(address user) internal {
        uint256 currentWeek = block.timestamp / 1 weeks;
        uint256 lastWeek = userLastEngagement[user] / 1 weeks;
        
        if (currentWeek > lastWeek) {
            weeklyEngagementCount[user] = 1;
            if (currentWeek == lastWeek + 1) {
                streakCount[user]++;
                if (streakCount[user] % 3 == 0) {
                    _distributeReward(user, EngagementType.STREAK_BONUS);
                }
            } else {
                streakCount[user] = 1;
            }
        } else {
            weeklyEngagementCount[user]++;
            if (weeklyEngagementCount[user] == 3) {
                _distributeReward(user, EngagementType.WEEKLY_ACTIVE);
            }
        }
        
        userLastEngagement[user] = block.timestamp;
    }

    function getAllRewardAmounts() external view returns (uint256[] memory) {
        uint256[] memory amounts = new uint256[](13);
        for(uint i = 0; i < 13; i++) {
            amounts[i] = rewardAmounts[EngagementType(i)];
        }
        return amounts;
    }

    function getFeedbackDetails(
        uint256 episodeId,
        uint256 feedbackIndex
    ) external view returns (
        address user,
        string memory content,
        bool isHidden,
        uint256 timestamp,
        uint256 reportCount,
        ModerationType moderationType
    ) {
        Feedback storage feedback = episodeFeedback[episodeId][feedbackIndex];
        return (
            feedback.user,
            feedback.content,
            feedback.isHidden,
            feedback.timestamp,
            feedback.reportCount,
            feedback.moderationType
        );
    }

    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "Invalid address");
        owner = newOwner;
    }
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):