Неправильно работает сохранения в файл python

У меня есть python aiogram код, который на команде /start спрашивает у пользователя имя и записывает в файл в формате "username - entered name", и команда /namechange, которая снова спрашивает у пользователя имя и перезаписывает его. Всё работает нормально до того момента, пока пишет 1 пользователь, если начинает писать второй пользователь, то функция замены имени ломается, и вместо перезаписи просто добавляет новое имя в конец файла, что вызывает несколько сообщений (вместо одного) типа "Привет {name}". Как можно это починить? Заранее спасибо.

from aiogram import Bot, types
from aiogram.dispatcher import Dispatcher
from aiogram.utils import executor
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
 
TOKEN='xxxxxxxxxxxxxxxxxxxxxxxxxxx'
bot = Bot(token=TOKEN)
storage=MemoryStorage()
dp = Dispatcher(bot, storage=storage)
 
class Form(StatesGroup):
    name = State()
    newname = State()
 
admins = open('admins.zailoxconf', 'r')
admins.seek(0)
blocked_users = open('blocked_users.zailoxconf', 'r')
blocked_users.seek(0)
names = open('names.zailoxconf', 'r')
names.seek(0)
#ALLOWED_USERS = ['username']
#ADMINS = ['username']
 
bur = blocked_users.read()
ar = admins.read()
nr = names.read()
 
@dp.message_handler(lambda msg: msg.from_user.username not in bur, commands=['check'])
async def check(message: types.Message):
    await message.answer("Вы не заблокированы")
 
@dp.message_handler(lambda msg2: msg2.from_user.username in bur, commands=['check'])
async def check2(message: types.Message):
    await message.answer("Вы заблокированы")
 
@dp.message_handler(lambda msg: msg.from_user.username, commands=['unblock'])
async def unblock(message: types.Message):
    global bur
    global blocked_users
    msg = message.text.replace('/unblock ', '')
    if msg == "2601":
        await message.answer('Разблокирован успешно')
        #ALLOWED_USERS.append(message.from_user.username)
        unblock = bur.replace(message.from_user.username, "")
        bur = blocked_users.read()
        blocked_users = open('blocked_users.zailoxconf', 'w')
        blocked_users.write(unblock)
        blocked_users = open('blocked_users.zailoxconf', 'r')
    else:
        await message.answer('Неверный код')
 
@dp.message_handler(lambda msg: msg.from_user.username not in bur and msg.from_user.username in ar, commands=['block'])
async def ban(message: types.Message):
    global bur
    global ar
    msg = message.text.replace("/block ", "")
    block = open('blocked_users.zailoxconf', 'a+')
    block.write(msg)
    block.close()
    blocked_users = open('blocked_users.zailoxconf', 'r')
    bur = blocked_users.read()
    await message.reply("Пользователь заблокирован ✓")
 
@dp.message_handler(lambda msg: msg.from_user.username in ar, commands=['pardon'])
async def unban(message: types.Message):
    global bur
    global blocked_users
    #ALLOWED_USERS.append(message.from_user.username)
    msg = message.text.replace("/pardon ", "")
    unblock = bur.replace(msg, "")
    bur = blocked_users.read()
    blocked_users = open('blocked_users.zailoxconf', 'w')
    blocked_users.write(unblock)
    blocked_users = open('blocked_users.zailoxconf', 'r')
    bur = blocked_users.read()
    await message.reply("Пользователь разблокирован ✓")
 
@dp.message_handler(lambda msg: msg.from_user.username not in bur and msg.from_user.username not in ar, commands=['block'])
async def unban(message: types.Message):
    await message.reply("Вы не админ")
 
@dp.message_handler(lambda msg: msg.from_user.username, commands=['sendall'])
async def sendall(message: types.Message):
    if message.from_user.username in admins: #ADMINS:
        for i in users:
            await bot.send_message(i,message.text[message.text.find(' '):])
        await message.answer('Done')
    else:
        await message.reply("Вы не админ")
 
@dp.message_handler(lambda msg: msg.from_user.username not in nr, commands=['start'])
async def start(message: types.Message):
    await Form.name.set()
    await message.reply("Как тебя зовут?\n/cancel - отмена")
 
@dp.message_handler(state='*', commands=['cancel'])
async def cancel_handler(message: types.Message, state: FSMContext):
    """Allow user to cancel action via /cancel command"""
 
    current_state = await state.get_state()
    if current_state is None:
        # User is not in any state, ignoring
        return
 
    # Cancel state and inform user about it
    await state.finish()
    await message.reply('Отменено.')
 
@dp.message_handler(state=Form.name)
async def process_name(message: types.Message, state: FSMContext):
    """Process user name"""
 
    # Finish our conversation
    await state.finish()
    await message.reply(f"Привет, {message.text}")  # <-- Here we get the name
    global name
    global names
    global nr
    name=message.text
    names = open('names.zailoxconf', 'a+')
    replaces = name.replace('\n', '')
    names.write(f"{message.from_user.username} - {replaces}\n")
    names.close()
    names = open('names.zailoxconf', 'r')
    nr = names.read()
 
@dp.message_handler(lambda msg: msg.from_user.username in nr, commands=['start'])
async def process_name(message: types.Message):
    global nr
    global names
    for item in nr.split("\n"):
        if message.from_user.username in item:
            await message.answer(f"С возвращением, {nr.strip().replace(f'{message.from_user.username} - ', '')}")
            names.close()
            names = open('names.zailoxconf', 'r')
            names.seek(0)
            nr = names.read()
 
@dp.message_handler(lambda msg: msg.from_user.username in nr, commands=['namechange'])
async def process_name(message: types.Message):
    await Form.newname.set()
    await message.reply("Отправьте новое имя\nОтмена - /cancel")
 
    @dp.message_handler(state='*', commands=['cancel'])
    async def cancel_handler(message: types.Message, state: FSMContext):
        """Allow user to cancel action via /cancel command"""
 
        current_state = await state.get_state()
        if current_state is None:
            # User is not in any state, ignoring
            return
 
        # Cancel state and inform user about it
        await state.finish()
        await message.reply('Отменено.')
 
@dp.message_handler(state=Form.newname)
async def process_name(message: types.Message, state: FSMContext):
    """Process user name"""
 
    # Finish our conversation
    await state.finish()
    global name
    global names
    global nr
    name = nr.strip().replace(f'{message.from_user.username} - ', '')
    newname = message.text
    oldname = name
    names = open('names.zailoxconf', 'a+')
    names.seek(0)
    replaces = newname.replace('\n', '')
    names.close()
    names = open('names.zailoxconf', 'r')
    names.seek(0)
    b = nr.replace(f'{message.from_user.username} - {oldname}', '')
    names = open('names.zailoxconf', 'w')
    names.seek(0)
    names.write(f"{b}\n")
    names = open('names.zailoxconf', 'a+')
    names.write(f"{message.from_user.username} - {replaces}\n")
    names = open('names.zailoxconf', 'r')
    names.seek(0)
    nr = names.read()
    name = message.text
    await message.reply(f"Новое имя - {name}")  # <-- Here we get the name
 
@dp.message_handler(lambda msg: msg.from_user.username not in nr, commands=['namechange'])
async def process_name(message: types.Message):
    await message.reply("Сначала выберите имя /start")
 
async def on_startup(_):
    print("Бот успешно запущен")
 
def blankremove():
    global names
    names = open('names.zailoxconf', 'r')
    lines = names.readlines()
    non_empty_lines = (line for line in lines if not line.isspace())
    names = open('names.zailoxconf', 'w')
    names.close()
    names = open('names.zailoxconf', 'r+')
    names.writelines(non_empty_lines)
 
if __name__ == '__main__':
    executor.start_polling(dp, on_startup=on_startup)
    admins.close()
    blocked_users.close()
    blankremove()
    names.close()
    print('Бот успешно остановлен')

Ответы (1 шт):

Автор решения: Maxim

Лучшим решением будет писать данные в БД. Если это "игрушечный" проект, то подойдет SQLite. Там необходимо создать таблицу users и в нее добавлять пользователя через INSERT, или удалять через DELETE (редактировать через UPDATE) + будет правильно создать ещё таблицы, в которых хранить заблокированных пользователей и т.д.

Если пытаться делать через файл, то во-первых, лучше всего открыть его один раз и на чтение и на запись, а не открывать каждый раз (важно ещё каждый раз не забывать тогда закрывать), и закрывать при завершении работы бота. Для замены пользователей использовать поиск по файлу по имени пользователя (он вернет положение каретки начала) и перезаписывать всю строку целиком.

→ Ссылка