<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Verse - 次世代ソーシャルネットワーク</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/rita/1.3.63/rita-full.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 0; background: #f0f0f0; }
header, .footer { background: linear-gradient(45deg, #6a11cb, #2575fc); color: white; text-align: center; padding: 20px; }
.nav-container { background: #2575fc; display: flex; justify-content: center; padding: 10px; position: sticky; top: 0; }
.nav-menu a { color: white; text-decoration: none; margin: 0 10px; }
.content { max-width: 900px; margin: 20px auto; background: white; padding: 20px; border-radius: 8px; }
.cta-button { background: #2575fc; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; }
.profile-icon { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; }
.dark-mode { background: #1e1e1e; color: #ddd; }
.timeline { margin-top: 20px; }
.timeline-post { border: 1px solid #ddd; border-radius: 8px; padding: 10px; margin-bottom: 10px; background: #fff; }
.post-controls { display: flex; gap: 10px; }
.search-box { width: 100%; margin-bottom: 10px; }
</style>
<script>
let posts = JSON.parse(localStorage.getItem('posts') || '[]');
let feedUrls = JSON.parse(localStorage.getItem('feedUrls') || '[]');
let botInterval = null;
let feedInterval = null;
function saveData() {
localStorage.setItem('posts', JSON.stringify(posts));
localStorage.setItem('feedUrls', JSON.stringify(feedUrls));
}
function createPost(content) {
posts.unshift({ content: content, likes: 0 });
saveData();
renderTimeline();
}
function createUserPost() {
const content = document.getElementById('postContent').value.trim();
if(content) {
createPost(content);
document.getElementById('postContent').value = '';
}
}
function likePost(index) {
posts[index].likes++;
saveData();
renderTimeline();
}
function deletePost(index) {
posts.splice(index, 1);
saveData();
renderTimeline();
}
function searchPosts() {
const query = document.getElementById('searchInput').value.trim().toLowerCase();
renderTimeline(query);
}
function renderTimeline(filter = '') {
const container = document.getElementById('timeline');
container.innerHTML = '';
posts.filter(post => post.content.toLowerCase().includes(filter)).forEach((post, index) => {
const postDiv = document.createElement('div');
postDiv.className = 'timeline-post';
postDiv.innerHTML = `
<p>${post.content}</p>
<div class='post-controls'>
<button class='btn btn-sm btn-primary' onclick='likePost(${index})'>❤️ いいね (${post.likes})</button>
<button class='btn btn-sm btn-danger' onclick='deletePost(${index})'>🗑️ 削除</button>
</div>
`;
container.appendChild(postDiv);
});
}
function toggleDarkMode() {
document.body.classList.toggle('dark-mode');
}
function uploadProfileIcon(event) {
const file = event.target.files[0];
if(file) {
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('profile-icon').src = e.target.result;
showTimeline();
};
reader.readAsDataURL(file);
}
}
function showTimeline() {
document.getElementById('profile-section').style.display = 'none';
document.getElementById('post-section').style.display = 'block';
document.getElementById('timeline').style.display = 'block';
}
function showProfile() {
document.getElementById('profile-section').style.display = 'block';
document.getElementById('post-section').style.display = 'none';
document.getElementById('timeline').style.display = 'none';
}
function postBotMessage() {
const content = document.getElementById('botContent').value.trim();
if(content) {
createPost(`🤖 BOT: ${content}`);
document.getElementById('botContent').value = '';
}
}
function generateMarkovText() {
const combinedText = posts.map(p => p.content).join(' ');
if(combinedText.length < 20) return "BOTの投稿データが不足しています。";
const markov = new RiTa.Markov(3);
markov.addText(combinedText);
const sentences = markov.generate(1);
return sentences[0] ? sentences[0] : "自然な文章の生成に失敗しました。";
}
function postMarkovBot() {
const text = generateMarkovText();
createPost(`🤖 MarkovBOT: ${text}`);
}
function startBotAutoPost(intervalSec) {
if(botInterval) clearInterval(botInterval);
botInterval = setInterval(postMarkovBot, intervalSec * 1000);
alert(`マルコフ連鎖BOTの自動投稿間隔を${intervalSec}秒に設定しました。`);
}
function registerFeedUrl() {
const url = document.getElementById('feedUrl').value.trim();
if(url) {
feedUrls.push(url);
saveData();
alert('RSSフィードURLを登録しました。');
document.getElementById('feedUrl').value = '';
}
}
async function fetchAllFeeds() {
for(const url of feedUrls) {
try {
const response = await fetch(`https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(url)}`);
const data = await response.json();
data.items.forEach(item => {
createPost(`📡 FEED: ${item.title} - ${item.link}`);
});
} catch (error) {
console.error('RSSフィード取得エラー:', error);
}
}
}
function startFeedAutoPost(intervalSec) {
if(feedInterval) clearInterval(feedInterval);
feedInterval = setInterval(fetchAllFeeds, intervalSec * 1000);
alert(`RSSフィードの自動投稿間隔を${intervalSec}秒に設定しました。`);
}
window.onload = function() {
renderTimeline();
};
</script>
</head>
<body>
<header>
<h1>Verse</h1>
<button class="cta-button" onclick="toggleDarkMode()">🌙 ダークモード切替</button>
</header>
<div class="nav-container">
<div class="nav-menu">
<a href="#" onclick="showTimeline()">ホーム</a>
<a href="#" onclick="showProfile()">プロフィール</a>
</div>
</div>
<div class="content">
<section id="profile-section" style="display:none;">
<h2>プロフィール</h2>
<img id="profile-icon" src="https://via.placeholder.com/80" alt="プロフィールアイコン" class="profile-icon"><br><br>
<input type="file" accept="image/*" onchange="uploadProfileIcon(event)">
</section>
<section id="post-section">
<h2>新規投稿</h2>
<textarea id="postContent" class="form-control" placeholder="いま何してる?"></textarea><br>
<button class="cta-button" onclick="createUserPost()">投稿</button>
<h2>BOT投稿登録</h2>
<textarea id="botContent" class="form-control" placeholder="BOTに投稿させたい内容"></textarea><br>
<button class="cta-button" onclick="postBotMessage()">BOT投稿登録</button><br><br>
<input type="number" id="botIntervalSec" placeholder="BOTの自動投稿間隔(秒)">
<button class="cta-button" onclick="startBotAutoPost(parseInt(document.getElementById('botIntervalSec').value))">マルコフ連鎖 自動投稿開始</button><br><br>
<h2>RSSフィード登録</h2>
<input type="text" id="feedUrl" class="form-control" placeholder="RSSフィードのURL"><br>
<button class="cta-button" onclick="registerFeedUrl()">フィード登録</button><br><br>
<input type="number" id="feedIntervalSec" placeholder="RSS自動投稿間隔(秒)">
<button class="cta-button" onclick="startFeedAutoPost(parseInt(document.getElementById('feedIntervalSec').value))">RSS 自動投稿開始</button>
</section>
<input type="text" id="searchInput" class="form-control search-box" placeholder="投稿を検索..." onkeyup="searchPosts()">
<section id="timeline" class="timeline"></section>
</div>
<div class="footer">© 2025 Verse - 新しいつながりを創造する</div>
</body>
</html>